Using Argon2 in Lucee CFML
A couple of months ago I wrote about password hashing in CFML (if you haven’t read that, I would suggest reading it before reading this) and in the post, I mentioned that Argon2 as being the currently recommended algorithm for password hashing but that, at the time of writing, neither Adobe ColdFusion or Lucee natively supported the Argon2 algorithm. I submitted an enhancement request to Lucee via their JIRA ticket system and at the end of August, it was reported on the ticket that it had been implemented and support was available in build 5.3.8.43 or higher. Currently, this build is only available on the SNAPSHOT branch, but it is available to download and use if you want it, so I thought I would give it a try.
Lucee’s Argon2 functions
Lucee has added the following two functions to support the use of Argon2 hashing:
string GenerateArgon2Hash( string input , string variant [Argon2i, Argon2d or Argon2id] , int parallelismFactor [1 - 10] , int memoryCost [ 8 - 100000 ] , int iterations [ 1 - 20 ] )
This function is used to generate an Argon2 hash that you would then store in your database. Unlike the PBKDF2 version I showed in the previous article, there is no need with an Argon2 hash to store all the parameters with the hash, as they are all contained within the hash, the output will look something like:
$argon2i$v=19$m=64,t=1,p=1$W7SEywipzFzD847FdFzojQ$qr+tDogG/BaOL7NZCQ5f0Bb4I9KPZCP7g4npVo+AncA
To check this hash, the following function can be used:
boolean Argon2CheckHash( string input , string hash , string variant [Argon2i, Argon2d or Argon2id] )
The function returns either true or false depending on if the input string hashes to the same hash as the input hash, e.g. the hash stored in your database.
Argon2 Examples
Here is a code example showing how it works:
<cfscript> hash1 = GenerateArgon2Hash('test'); testHashResult1 = Argon2CheckHash('test', hash1); dump(hash1); dump(testHashResult1); hash2 = GenerateArgon2Hash('test', 'argon2d', 4, 250, 10); testHashResult2 = Argon2CheckHash('test', hash2, 'argon2d'); dump(hash2); dump(testHashResult2); hash3 = GenerateArgon2Hash('test', 'argon2id', 4, 500, 3); testHashResult3 = Argon2CheckHash('test', hash3, 'argon2id'); dump(hash3); dump(testHashResult3); </cfscript>
This is a simple example that just shows the functions in action. For details on how to implement in a real-world situation see my previous Password Hashing in CFML post.
Argon2 is designed to allow you to adjust the amount of “hashing” that is done using the parameters for memory, parallelism and iterations. These parameters allow you to find a “sweet spot” for the amount of time it takes to compute the hash versus the complexity of the hash. I recommend the following article for a good explanation of this: How to Choose the Right Parameters for Argon2.
Issues
After looking at these new functions I spotted a couple of issues which I have raised on the ticket:
- There is no “salt” parameter for the
GenerateArgon2Hash
function. My understanding is a salt is required for an Argon2 hash. If you take a look at https://argon2.online/ it will not let you generate an Argon2 hash without one. - The
Argon2CheckHash
function has an option to tell it which “variant” to use for the hashing, but like the other parameters passed intoGenerateArgon2Hash
, you don’t need to tell the check function the variant as it is part of the hash, right at the beginning, e.g.$argon2i$
The first of these I think is a show stopper, as if you use it today without a salt and then a “required” salt
attribute is added to the functions in future, then your existing hashes will become unusable, so I wouldn’t recommend using these until the salt
attribute is added to both functions.
UPDATE 22-09-2020: Seems I was partly wrong about this. The hashes Lucee generate actually includes an automatically generated random salt and this is included in the output string between the parameters and the hash itself, delimited with the dollar ($) symbol. In the example above, the salt is W7SEywipzFzD847FdFzojQ
.
Conclusion
It’s good to see Lucee is adding functions to enhance the security of applications created with it. Hopefully, they will fix the missing “salt” issue pretty quickly and then this function will be usable in real-world applications.
UPDATE 22-09-2020: If you are happy with randomly generated salts, which for most cases should be perfectly acceptable, then it is all good to use.
Andrew –
Thanks for the great articles and review of this. Do I understand correctly that unlike your “Password Hashing in CFML” post where you showed how to save the iterations, salt etc as a string in the DB and then parse them when checking the password, I now can skip all of this with GenerateArgon2Hash?
It is now pretty much just using Argon2CheckHash to compare the password with the stored string generated from GenerateArgon2Hash for either true or false? is that right?
Hi Scott,
Yes, that is correct, you store the result for GenerateArgon2Hash and then use the Argon2CheckHash function to check if the store hash matches the input. So the only thing you can’t do from my other article is add “pepper” as the “salt” is randomly generated by the function, but that is acceptable in my opinion.