Table of Contents

PHP RFC: Throw error for passwords longer than 72 bytes in password_hash() with bcrypt

Introduction

This RFC proposes to enforce input length limits in password_hash() for PASSWORD_BCRYPT by emitting a deprecation notice in PHP 8.7 and throwing a ValueError in PHP 8.8.

The PASSWORD_BCRYPT algorithm in PHP's password_hash() wraps standard bcrypt. Bcrypt operates on a fixed key schedule with a hard maximum password length of 72 bytes. Any input characters beyond byte 72 are silently discarded.

Historically, PHP has silently truncated inputs longer than 72 bytes. Passing longer strings to password_hash can result in severe authentication bypass vulnerabilities:

Proposal

When password_hash() is called with PASSWORD_BCRYPT (or PASSWORD_DEFAULT when resolving to bcrypt) and the input $password string exceeds 72 bytes in length:

Users don't use passwords longer than 72 bytes

The rockyou password dump contains 772 out of 14344391 passwords that are longer than 72 characters. However, many of those are obvious form spam, keyboard mashing, or just copy/pasted content, not actual passwords. I couldn't find an obvious actual password in the set. This is not conclusive, since passwords can be any string and don't have to look like passwords.

Adobe's 2013 breach contains 152989508 passwords encrypted with an 8-byte block cipher. The longest password is 11 blocks (88 bytes), but it's the same block 10 times, indicating a repetitive password. There's one password that takes up 9 blocks (72 bytes), and several that take up 7 blocks (56 bytes).

So depending on how you look at it, passwords longer than 72 characters occur between never and 0.005% of the time.

Why ValueError applies instead of generic Exception

In PHP's exception hierarchy, a ValueError indicates a programming error rather than a runtime operational/input error that an application should catch and recover from dynamically.

One might argue that receiving a long string is a dynamic runtime input error. However, throwing a ValueError is appropriate here because exceeding 72 bytes in password_hash() is fundamentally a programming error for two reasons:

Throwing a ValueError ensures developers catch this misconfiguration during development and testing rather than allowing insecure hashing patterns into production.

Why password_verify() is left unchanged

This RFC explicitly does not modify password_verify().

Applying a failure or exception to password_verify() for inputs exceeding 72 bytes would introduce a severe backward-compatibility break. Existing users who registered under previous versions with passphrases longer than 72 bytes (or whose applications prepended long secrets) would be permanently unable to authenticate. Leaving password_verify() untouched preserves access for existing credentials while preventing the creation of new truncated hashes.

Examples

// Valid passwords (<= 72 bytes) work normally across all versions:
$hash = password_hash('correct-horse-battery-staple', PASSWORD_BCRYPT);
 
// Passwords > 72 bytes:
 
// PHP 8.7 Behavior:
// E_DEPRECATED: password_hash(): Passing a password longer than 72 bytes to the bcrypt algorithm is deprecated
$longPassword = str_repeat('a', 80);
$hash = password_hash($longPassword, PASSWORD_BCRYPT);
 
// PHP 8.8+ Behavior:
try {
    $hash = password_hash($longPassword, PASSWORD_BCRYPT);
} catch (ValueError $e) {
    // Throws: ValueError: password_hash(): Password length cannot exceed 72 bytes when using PASSWORD_BCRYPT
}

Backward Incompatible Changes

Applications allowing long passphrases or prepending pepper/nonces should validate password length, pre-hash input (e.g. using raw SHA-256/SHA-512 outputs before passing to bcrypt), or migrate to algorithms without 72-byte restrictions, such as Argon2id (PASSWORD_ARGON2ID).

Proposed PHP Version(s)

RFC Impact

To the Ecosystem

Frameworks and authentication libraries that permit arbitrary-length input strings or prepend pepper secret strings will need to enforce length validation or pre-hashing before calling password_hash().

To Existing Extensions

None.

To SAPIs

None.

Discussion

Opposition arguments:

Support arguments:

Open Issues

None at present.

Future Scope

Voting Choices

Primary Vote: Requires 2/3 majority.

Throw error on bcrypt passwords longer than 72 bytes as described in the RFC?
Real name Yes No Abstain
Final result: 0 0 0
This poll has been closed.

Patches and Tests

References

Changelog