From 34c7e0c1e7eeef2a301d59384b8b94e6ee6aac1c Mon Sep 17 00:00:00 2001 From: "Florine W. Dekker" Date: Tue, 13 Sep 2022 22:29:10 +0200 Subject: [PATCH 1/3] Add footnote warning to hashing a hash --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index bbbbd77c..f215cf2f 100644 --- a/README.rst +++ b/README.rst @@ -231,6 +231,8 @@ encode it to prevent NULL byte problems before hashing the result with ... bcrypt.gensalt() ... ) +Note, however, that this practice is generally `recommended against`_, as it may expose the system to `hash shucking`_ attacks, and to denial of service attacks if an attacker rapidly sends many extremely long passwords. + Compatibility ------------- @@ -252,3 +254,5 @@ identify a vulnerability, we ask you to contact us privately. .. _`standard library`: https://docs.python.org/3/library/hashlib.html#hashlib.scrypt .. _`argon2_cffi`: https://argon2-cffi.readthedocs.io .. _`cryptography`: https://cryptography.io/en/latest/hazmat/primitives/key-derivation-functions/#cryptography.hazmat.primitives.kdf.scrypt.Scrypt +.. _`recommended against`: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pre-hashing-passwords +.. _`hash shucking`: https://security.stackexchange.com/a/234795/ From a4497b2c3da66fb7cc5c8a20afdd2887de340df0 Mon Sep 17 00:00:00 2001 From: "Florine W. Dekker" Date: Wed, 14 Sep 2022 16:10:44 +0200 Subject: [PATCH 2/3] Remove ddos mention, use bcrypt_pbkdf --- README.rst | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index f215cf2f..17ba3bd3 100644 --- a/README.rst +++ b/README.rst @@ -217,21 +217,28 @@ As of 3.0.0 the ``$2y$`` prefix is still supported in ``hashpw`` but deprecated. Maximum Password Length ~~~~~~~~~~~~~~~~~~~~~~~ -The bcrypt algorithm only handles passwords up to 72 characters, any characters +The bcrypt algorithm only handles passwords up to 72 characters; any characters beyond that are ignored. To work around this, a common approach is to hash a -password with a cryptographic hash (such as ``sha256``) and then base64 -encode it to prevent NULL byte problems before hashing the result with +password with a keyed cryptographic hash (such as ``bcrypt_pbkdf``) and then +base64 encode it to prevent NULL byte problems before hashing the result with ``bcrypt``: .. code:: pycon + >>> import base64 + >>> import bcrypt >>> password = b"an incredibly long password" * 10 >>> hashed = bcrypt.hashpw( - ... base64.b64encode(hashlib.sha256(password).digest()), + ... base64.b64encode(bcrypt.kdf(password=password, + ... salt=pepper, + ... desired_key_bytes=32, + ... rounds=100)), ... bcrypt.gensalt() ... ) -Note, however, that this practice is generally `recommended against`_, as it may expose the system to `hash shucking`_ attacks, and to denial of service attacks if an attacker rapidly sends many extremely long passwords. +Using a hash function without a hash is `recommended against`_ as it may expose +the system to `hash shucking`_ attacks. Instead, the hash function should use a +global `pepper`_ or a per-hash salt. Compatibility ------------- @@ -256,3 +263,4 @@ identify a vulnerability, we ask you to contact us privately. .. _`cryptography`: https://cryptography.io/en/latest/hazmat/primitives/key-derivation-functions/#cryptography.hazmat.primitives.kdf.scrypt.Scrypt .. _`recommended against`: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pre-hashing-passwords .. _`hash shucking`: https://security.stackexchange.com/a/234795/ +.. _`pepper`: https://en.wikipedia.org/wiki/Pepper_(cryptography) From ee8a991fbb837f76c7711a00dc63fab293d00bfc Mon Sep 17 00:00:00 2001 From: "Florine W. Dekker" Date: Wed, 30 Aug 2023 13:48:56 +0200 Subject: [PATCH 3/3] Rewrite section on maximum password length --- README.rst | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 17ba3bd3..ba8843c6 100644 --- a/README.rst +++ b/README.rst @@ -218,27 +218,31 @@ Maximum Password Length ~~~~~~~~~~~~~~~~~~~~~~~ The bcrypt algorithm only handles passwords up to 72 characters; any characters -beyond that are ignored. To work around this, a common approach is to hash a -password with a keyed cryptographic hash (such as ``bcrypt_pbkdf``) and then -base64 encode it to prevent NULL byte problems before hashing the result with -``bcrypt``: +beyond that are ignored. The best solution to this problem is to stop using +bcrypt and use a modern algorithm such as argon2id or scrypt. Seriously. + +If you must use bcrypt, you can work around bcrypt's character limit by first +hashing the password with a hexadecimal salted cryptographic hash. Note that +omitting the salt or using raw output is `recommended against`_ because it may +expose the system to `hash shucking`_ attacks. Therefore, make sure you give the +inner hash function a `pepper`_, and encode the output as base64 to prevent +`NULL`-byte problems: .. code:: pycon >>> import base64 >>> import bcrypt + >>> import hmac >>> password = b"an incredibly long password" * 10 + >>> pepper = bcrypt.gensalt() # Do not store the pepper in your database >>> hashed = bcrypt.hashpw( - ... base64.b64encode(bcrypt.kdf(password=password, - ... salt=pepper, - ... desired_key_bytes=32, - ... rounds=100)), + ... base64.b64encode(hmac.digest(pepper, password, "sha256")), ... bcrypt.gensalt() ... ) - -Using a hash function without a hash is `recommended against`_ as it may expose -the system to `hash shucking`_ attacks. Instead, the hash function should use a -global `pepper`_ or a per-hash salt. + >>> matches = bcrypt.checkpw( + ... base64.b64encode(hmac.digest(pepper, password, "sha256")), + ... hashed + ... ) Compatibility -------------