[PHP-DEV] [RFC] Throw error for passwords longer than 72 bytes in password_hash() with bcrypt

Hello,

I have drafted a new RFC: https://wiki.php.net/rfc/bcrypt_max_password_length

The proposal is to throw a ValueError when a password longer than 72 characters is passed to password_hash and bcrypt is used. The current behavior is that the password is silently truncated and only the first 72 characters are hashed.

The goal is to prevent severe security vulnerabilities that are the result of this silent truncation. This will primarily impact applications that pass something else than just the user’s password to password_hash. Please let me know what you think!

Regards,

Sjoerd

On Tue, 29 Sept 2026 at 12:35, Sjoerd Langkemper
<sjoerd-php@linuxonly.nl> wrote:

Hello,

I have drafted a new RFC: PHP: rfc:bcrypt_max_password_length

The proposal is to throw a ValueError when a password longer than 72 characters is passed to password_hash and bcrypt is used. The current behavior is that the password is silently truncated and only the first 72 characters are hashed.

The goal is to prevent severe security vulnerabilities that are the result of this silent truncation. This will primarily impact applications that pass something else than just the user's password to password_hash. Please let me know what you think!

Regards,

Sjoerd

Checking that the input is less than 72 bytes long is the
application's responsibility, not the algorithm's. The algorithm is
designed in a way that you don't need to worry about too-long
passphrases as an application developer. Vulnerabilities come from
developers trying to outsmart the algorithm by modifying passwords
before hashing or by providing something other than a
password/passphrase as an input. No change to the algorithm is going
to prevent that.

By adding a ValueError which only fires when the input is too long,
you are introducing a silent failure vector that is difficult to catch
or test for. If an application doesn't limit user password length to
72 bytes, there will be users that will try such passwords and the
application will crash for them instead of working correctly as
before. And since an overlong password is an acceptable input, that
error is not going to help neither the user or the developer.

Such a limitation would only help in really egregious cases of misuse,
when a developer prepends an almost 72-byte string to a password or
passes something other than a password as input. Both should be caught
in a code review by a senior developer, not runtime.

Hi

On 2026-09-29 14:04, Kamil Tekiela wrote:

[…]

I agree with that in full.

Best regards
Tim Düsterhus

On Tue, Sep 29, 2026, at 14:04, Kamil Tekiela wrote:

On Tue, 29 Sept 2026 at 12:35, Sjoerd Langkemper wrote:

The proposal is to throw a ValueError when a password longer than 72 characters is passed to password_hash and bcrypt is used.

Checking that the input is less than 72 bytes long is the
application’s responsibility, not the algorithm’s.

Thank you for your feedback. It is clear to me that you oppose my proposal as is. Do you have a suggestion for improvement? Something I can change that would restrict misuse of password_hash but would have your approvement?

there will be users that will try such passwords and the
application will crash for them instead of working correctly as
before.

Yes, this can happen. This is an intentional tradeoff: my proposed change makes the situation more secure, but could crash applications in some situations. I described in the RFC that users rarely/never use passwords longer than 72 characters, so I think this is acceptable.

Such a limitation would only help in really egregious cases of misuse,
when a developer prepends an almost 72-byte string to a password or
passes something other than a password as input. Both should be caught
in a code review by a senior developer, not runtime.

The FreshRSS case is interesting here. Each change was reviewed and seemed secure, but the combination resulted in authentication bypass. https://pentesterlab.com/blog/freshrss-bcrypt-truncation-auth-bypass

Regards,

Sjoerd

On 29 September 2026 13:04:21 BST, Kamil Tekiela <tekiela246@gmail.com> wrote:

By adding a ValueError which only fires when the input is too long,
you are introducing a silent failure vector that is difficult to catch
or test for.

On the contrary, it turns a silent failure into a noisy one, prompting the developer to take action.

If an application doesn't limit user password length to
72 bytes, there will be users that will try such passwords and the
application will crash for them instead of working correctly as
before.

Such a system was not working correctly before - it was accepting passwords that it could not verify later, and consequently accepting logins which did not match the user's intended password.

And since an overlong password is an acceptable input

Why is it an acceptable input? If the algorithm can't correctly hash that input, why should it tell the user it has done so?

It would be a problem if users who have *already* set passwords which they intended to be longer than 72 bytes are prevented from logging in, but the proposal covers that by leaving password_verify unchanged.

Both should be caught in a code review no a senior developer, not runtime.

If every PHP login implementation was reviewed by an expert senior developer, we would not need the password_* API in the first place. The value of this API is that it makes doing the right thing easy, so that you *don't* need to be an expert in the underlying algorithms to use it safely.

I support the RFC as currently proposed.

Rowan Tommins
[IMSoP]

Hi

On 2026-09-29 15:03, Sjoerd Langkemper wrote:

there will be users that will try such passwords and the
application will crash for them instead of working correctly as
before.

Yes, this can happen. This is an intentional tradeoff: my proposed change makes the situation more secure, but could crash applications in some situations. I described in the RFC that users rarely/never use passwords longer than 72 characters, so I think this is acceptable.

The limit is 72 *bytes*, not 72 *characters*. This is a meaningful difference: It means that users might be presented an error for non-ASCII passwords.

An 8-word Diceware password comes in at 100 bits of entropy and is roughly around 72 bytes in length. A 9-word password would exceed the limit, but the extra entropy above 100 bits is not really meaningful with regard to security, so just ignoring that extra word is fine.

Such a limitation would only help in really egregious cases of misuse,
when a developer prepends an almost 72-byte string to a password or
passes something other than a password as input. Both should be caught
in a code review by a senior developer, not runtime.

The FreshRSS case is interesting here. Each change was reviewed and seemed secure, but the combination resulted in authentication bypass. How "Strengthening Crypto" Broke Authentication: FreshRSS and bcrypt's 72-Byte Limit

The specified authentication protocol with “client side hashing” is a classic case of “rolling your own crypto” and it exposes the BCrypt salt to the client, likely to everyone who asks, since it clearly is pre-auth. I disagree with calling that part “seemingly secure”.

Even the updated nonce-generation, while better than before due to the use of the CSPRNG, includes needless security theater. The `random_bytes()` is what makes the nonce secure. Neither the system salt nor the username needs to be included and the SHA-256 hash just acts as a PRF, so saying “SHA-256 is stronger than SHA-1” is correct, but also utterly meaningless.

The write-up summarizes it well: “Over-engineering can hurt security”. The issue was not caused by the BCrypt truncation, it was caused my multiple problematic decisions. The latter includes the BCrypt truncation, but that ship has sailed and trying to enforce limits that BCrypt itself does not is making the situation worse.

Best regards
Tim Düsterhus

Hi

On 2026-09-29 15:36, Rowan Tommins [IMSoP] wrote:

On 29 September 2026 13:04:21 BST, Kamil Tekiela <tekiela246@gmail.com> wrote:

By adding a ValueError which only fires when the input is too long,
you are introducing a silent failure vector that is difficult to catch
or test for.

On the contrary, it turns a silent failure into a noisy one, prompting the developer to take action.

There is no silent failure here. BCrypt acts according to its specification.

[…] it was accepting passwords that it could not verify later […]

I assume it is ambiguous phrasing, but to be clear: Any passwords accepted by password_hash() will verify with password_verify().

And since an overlong password is an acceptable input

Why is it an acceptable input? If the algorithm can't correctly hash that input, why should it tell the user it has done so?

It would be a problem if users who have *already* set passwords which they intended to be longer than 72 bytes are prevented from logging in, but the proposal covers that by leaving password_verify unchanged.

The login will start to fail when the password is being rehashed (password_needs_rehash()) due to a change in the algorithm parameters, such as when increasing the (default) BCrypt cost.

Both should be caught in a code review no a senior developer, not runtime.

If every PHP login implementation was reviewed by an expert senior developer, we would not need the password_* API in the first place. The value of this API is that it makes doing the right thing easy, so that you *don't* need to be an expert in the underlying algorithms to use it safely.

The API is safe if you pass a “password” to it (as the name indicates). The issues described in the RFC were caused by folks passing something that is not a password.

Best regards
Tim Düsterhus