-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix Argon2 descriptions #23319
Fix Argon2 descriptions #23319
Conversation
The threads option for the password_hash function does not define the maximum allowed number of CPU threads to be used by the hashing algorithm but the exact number of threads that is used. Similarly the memory_cost option for the password_hash function does not define the maximum allowed memory to be used by the hashing algorithm, but the exact amount of memory that is used by the hashing table. The minimum value is 8 KiB per thread. The time_cost option for the password_hash function does not define the allowed time in seconds, but the number of iterations for the hash function. If the minimum values are understood, the minimum values are used instead. Signed-off-by: MichaIng <micha@dietpi.com>
🤖 beep boop beep 🤖 Here are the logs for the failed build: Status of 33918: failuresqliteShow full log
mariadb10.1-php7.3
mariadb10.4-php7.4
mysql8.0-php7.4
mysql5.6-php7.3
postgres9-php7.3Show full log
postgres11-php7.4
|
@charlesportwoodii I understand you are the author of the PHP RFC on Argon2 https://wiki.php.net/rfc/argon2_password_hash I take that the descriptions especially on the cost factors from the RFC are correct. However, there are some doubts on the Internet whether all factors are the maximum or the exact value, and specifically on time whether it is about the actual time or iterations. Maybe you can confirm that what the RFC states is indeed matching with the reality? :) |
@blizzz: @MichaIng assessment, and the description in the original RFC is accurate. The values correspond to the C libs
From the RFC:
In practice:
Will do Argon2ID with 3 iterations, consume 262144 KiB (268 MegaBytes), and use 4 CPU threads. You can observe this in top/htop or any other process monitoring tool pretty easily. As you can imagine this is a pretty fantastic way to kill your server, especially if you're processing 1000s of logins per minute. The PHP Password hash defaults are pretty low to compensate for this, particularly to ensure that PHP can run on pretty much any platform, which is why it's critical to have a test script running on your end platform to target the 1000ms+ hashing time for Argon2id, otherwise you should just be using Bcrypt, which has better hardness at sub 500ms. My recommendation for password hashing is:
In both cases, adjust the cost factors until you reach the desired timing. The PHP doc team isn't very involved in the RFC process - they normally write the docs after the RFC is approved and closer to the release window for new features so it's likely the descriptions were carried over wrong from the RFC. I'd advise making a PR to the PHP Wiki to update the description. They're not wrong in that it is the maximum values that will be used but I can certainly see how that could be misleading to think it that implies "up to 1020 KiB" rather than 'it will use exactly 1024 KiB" The only change I'd make to this PR in particular is that the memory cost must be a power of 2, hence why all the examples use bitwise shifting to calculate the value rather than plopping static KiB numbers in. |
Many thanks for your clarification and recommendations. I'm happy that my thoughts were not totally dumb at this topic and things indeed are as I thought make sense 😄.
Most interesting is the recommendation to use hashing times > 1 second. For me this would be just the upper limit for a seamless user experience. @blizzz And probably there should be an option to disable Argon2 (fallback to bcrypt) if short times are wanted. Although I'm still not sure which tasks really include this hasher, login + changing user password, as well authentication tokens, i.e. all sort of client connections, other tasks? |
@MichaIng The 2^n KiB recommendation comes from the C lib. The underlying C library doesn't validate the input for 2^n though, hence the recommendation to use bit-wise shifting to calculate the value since you guarantee at 2^n value. The 1s comes from the Argon2 authors https://twitter.com/terahashcorp/status/1155129705034653698. 1000ms should be the lower bound for any Argon2id hashing, if you're targeting less than that Bcrypt is objectively better in terms of hashing strength. If page load time for login is an important, just use Bcrypt with a 500ms target time and call it a day. It's a stronger and faster choice at 500ms Paragonie has a refiner for determining optimal costs based upon your system assuming single-threaded applications ( |
Thank you, Charles, for the insights!
Seconded! The >=1000ms is an absolute recommendation, so more threads/CPUs do not help here, I understand. In this case, switching back to bcrypt as default seems to be reasonable. We have a hasher PHP API which is used in various places where passwords are necessary, e.g. Talk rooms, sharing, local users but there is also a use case around LDAP connection handling (though we can solve this differently). @rullzer you'll want to know about this as well ^ |
Reading through Twitter discussion basically underlines the recommendation to not use Argon2 for password hashing, but bcrypt instead. I ran a few tests with default PHP values on very different machines for Argon2 (
Both of course not representative but just to mention that in both cases Argon2 is not a good choice, as on RPi2 it is too slow for a nice user experience and on the VPS+VM it is too fast to have benefit over bcrypt. And there is no good way to automatically apply better settings, aside of doing a bunch of tests and prompt an admin panel warning, as those highly depend on the system, available CPU cores and performance, available memory (i.e. what is expected to be used by PHP) and tests even on very current server load, making all this a bid unreliable if not the admin runs a set of tests to optimise the options. What I do not yet understand is why the >=1s recommendation is absolut when taking into account parallelism. I mean 4 threads can do one iteration through the same array four times faster. Of course it means that someone who wants to break the hash as well can test a string four times faster. But understanding the depth of hashing algorithms and their strength is going OT here 😄. Regarding the options descriptions. I'm not sure about the effect of using a memory cost value that is no power of 2, since the function returns a hash, but I can add the information that a power of two should be used. On hasher side, we could switch to bit-wise values or, to not break existing configs, override an invalid value with the nearest (or next lower) power of two + print a warning to logs, or such. |
I felt free to give this a milestone (+ vote for backport to next Nextcloud 20) and bump it a bid, since it is basically a security-related topic. Based on wrong information admins do wrong choices, which is horrible in case of password hashing. Switching back to bcrypt as default is another topic, but since we have Argon2 support now, at least the docs and comments about it should be correct, so that admins can do better choices. |
Companion commit for: nextcloud/server#23319 Signed-off-by: MichaIng <micha@dietpi.com>
I opened a PR to change the documentation accordingly: nextcloud/documentation#5828 |
/backport to stable20 |
/backport to stable19 |
The
threads
option for thepassword_hash
function does not define the maximum allowed number of CPU threads to be used by the hashing algorithm but the exact number of threads that is used.Similarly the
memory_cost
option for thepassword_hash
function does not define the maximum allowed memory to be used by the hashing algorithm, but the exact amount of memory that is used by the hashing table. The minimum value is 8 KiB per thread: ad60619#diff-8d8bf62389a253975b815495978348f9Most importantly, the
time_cost
option for thepassword_hash
function does not define the allowed time in seconds, but the number of iterations for the hash function!If the minimum values are understood, the minimum values are used instead: ad60619#diff-8d8bf62389a253975b815495978348f9
For reference: #19023 (comment)
That is something that is misunderstood in very most documentations, including the PHP function documentation: https://www.php.net/manual/en/function.password-hash.php
And the defaults variable documentation: https://www.php.net/manual/en/password.constants.php
And even on Wikipedia and some other sources it is at least described in a way that can be misunderstood.
Only the RFC itself describes it correctly: https://wiki.php.net/rfc/argon2_password_hash
This includes the fact that
time_cost
means number of times the hash algorithm iterates and the fact that those numbers are no maxima but the exactly used threads, memory and iterations. The result and the ability to reproduce and compare hashes relies on the fact that those three numbers are exactly what they are.I hope that some others can verify this, at best we check back with RFC author and based on that start to offer corrections for PHP docs and other places.
Related documentation change: nextcloud/documentation#5828