diff --git a/changelog.d/19078.bugfix b/changelog.d/19078.bugfix new file mode 100644 index 00000000000..0046afcccf1 --- /dev/null +++ b/changelog.d/19078.bugfix @@ -0,0 +1 @@ +Fix a bug introduced in 1.140.0 where an internal server error could be raised when hashing user passwords that are too long. \ No newline at end of file diff --git a/synapse/_scripts/hash_password.py b/synapse/_scripts/hash_password.py index 2b7d3585cb9..6a87303fc95 100755 --- a/synapse/_scripts/hash_password.py +++ b/synapse/_scripts/hash_password.py @@ -73,8 +73,18 @@ def main() -> None: pw = unicodedata.normalize("NFKC", password) + bytes_to_hash = pw.encode("utf8") + password_pepper.encode("utf8") + if len(bytes_to_hash) > 72: + # bcrypt only looks at the first 72 bytes + print( + f"Password is too long ({len(bytes_to_hash)} bytes); truncating to 72 bytes for bcrypt. " + "This is expected behaviour and will not affect a user's ability to log in. 72 bytes is " + "sufficient entropy for a password." + ) + bytes_to_hash = bytes_to_hash[:72] + hashed = bcrypt.hashpw( - pw.encode("utf8") + password_pepper.encode("utf8"), + bytes_to_hash, bcrypt.gensalt(bcrypt_rounds), ).decode("ascii") diff --git a/synapse/handlers/auth.py b/synapse/handlers/auth.py index 2d1990cce5b..f4583e33c3d 100644 --- a/synapse/handlers/auth.py +++ b/synapse/handlers/auth.py @@ -1683,8 +1683,22 @@ def _do_hash() -> str: # Normalise the Unicode in the password pw = unicodedata.normalize("NFKC", password) + bytes_to_hash = pw.encode( + "utf8" + ) + self.hs.config.auth.password_pepper.encode("utf8") + if len(bytes_to_hash) > 72: + # bcrypt only looks at the first 72 bytes. + # + # Note: we explicitly DO NOT log the length of the user's password here. + logger.debug( + "Password is too long; truncating to 72 bytes for bcrypt. " + "This is expected behaviour and will not affect a user's ability to log in. 72 bytes is " + "sufficient entropy for a password." + ) + bytes_to_hash = bytes_to_hash[:72] + return bcrypt.hashpw( - pw.encode("utf8") + self.hs.config.auth.password_pepper.encode("utf8"), + bytes_to_hash, bcrypt.gensalt(self.bcrypt_rounds), ).decode("ascii")