From 7aca2df08a4570dd86333239d52e62b108ef81ee Mon Sep 17 00:00:00 2001 From: Raphael Ahrens Date: Thu, 28 Nov 2024 11:26:30 +0100 Subject: [PATCH] Updated the bcrypt description as discussed in #1532 In #1532 there was a side discussion by @ja2048 and @jmanico on why the CS mentioned that HMAC would be vulnerable to password shucking, but the linked talk mentioned that even a hash with a pepper would prevent it. So I correcteed the guidance. I further removed the mentioning of PBKDF2, since this suggests that bcrypt + PBKDF2 would give FIPS-140 compliance. I also added that bcrypt should only be used for legacy system which don't have agron2 or scrypt, as discussed in #1532 --- cheatsheets/Password_Storage_Cheat_Sheet.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/cheatsheets/Password_Storage_Cheat_Sheet.md b/cheatsheets/Password_Storage_Cheat_Sheet.md index 7f275c5b34..9db8f4c4a6 100644 --- a/cheatsheets/Password_Storage_Cheat_Sheet.md +++ b/cheatsheets/Password_Storage_Cheat_Sheet.md @@ -119,7 +119,7 @@ These configuration settings provide an equal level of defense. The only differe ### bcrypt -The [bcrypt](https://en.wikipedia.org/wiki/bcrypt) password hashing function should be the best choice for password storage in legacy systems or if PBKDF2 is required to achieve FIPS-140 compliance. +The [bcrypt](https://en.wikipedia.org/wiki/bcrypt) password hashing function **should only** be used for password storage in legacy systems where Argon2 and scrypt are not available. The work factor should be as large as verification server performance will allow, with a minimum of 10. @@ -129,7 +129,20 @@ bcrypt has a maximum length input length of 72 bytes [for most implementations]( #### Pre-Hashing Passwords with bcrypt -An alternative approach is to pre-hash the user-supplied password with a fast algorithm such as SHA-256, and then to hash the resulting hash with bcrypt (i.e., `bcrypt(base64(hmac-sha256(data:$password, key:$pepper)), $salt, $cost)`). This is a dangerous (but common) practice that **should be avoided** due to [password shucking](https://www.youtube.com/watch?v=OQD3qDYMyYQ) and other issues when [combining bcrypt with other hash functions](https://blog.ircmaxell.com/2015/03/security-issue-combining-bcrypt-with.html) (**Note:** "The Fix" in this blog post only protect you from null bytes; it does **NOT** protect you from password shucking!). +An alternative approach is to pre-hash the user-supplied password with a fast algorithm such as SHA-2, HMAC, or BLAKE3 and then to hash the resulting hash value with bcrypt (i.e., `bcrypt(H($password)), $salt, $cost)`).. +This can be **dangerous** because of null bytes in the hash output value and because of [password shucking](https://www.youtube.com/watch?v=OQD3qDYMyYQ). + +The original bcrypt expects a null terminated password string, this means that the hash value will only be used to the first null byte in the hash value. (`bcrypt(H($password)), $salt, $cost) == bcrypt("", $salt, $cost)` if `H($password)[0] == 0`) +This increases the chance of finding a collision when [combining bcrypt with other hash functions](https://blog.ircmaxell.com/2015/03/security-issue-combining-bcrypt-with.html) and can be avoided by encoding the hash value to printable string with something like base64. +base64 can increases the length of the hash value above 72 characters and so there is a bit of truncation for large hash values from hashes like SHA-512, this is [negligible](https://soatok.blog/2024/11/27/beyond-bcrypt/). + +Password shucking uses the fact, that it is easy to check if `bcrypt(base64(H($password))), $salt, $cost) == bcrypt(base64($leaked_hash), $salt, $cost)`. +If the inner hash function `H` is used with the same password somewhere else and known to an attacker cracking the password can be reduced to breaking the hash function `H`. +Just using pure SHA-512, ( i.e. `bcrypt(base64(sha512($password))), $salt, $cost)`) is a **dangerous practice** and is as secure as just using pure SHA-512. +Password shucking only works if a leaked hash is known to the attacker, either through a breach database or rainbow tables. +To mitigate password shucking a [pepper](#peppering) can be used. + +To summarize if bcrypt has to be used and the password should to be pre-hashed you should do `bcrypt(base64(hmac-sha384(data:$password, key:$pepper)), $salt, $cost)` and store the pepper not in the database. ### PBKDF2