Table of Contents

PHP RFC: Deprecate and remove support 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 (derived from Openwall's crypt_blowfish implementation). 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 during key derivation and cost calculations.

Historically, PHP has silently truncated inputs longer than 72 bytes. When applications misuse password_hash()—for example, by prepending static salts, pepper nonces, or long identifiers to passwords before hashing—this silent truncation 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:

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 catastrophic backward-compatibility break. Existing users who registered under legacy 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.

Why ValueError applies instead of generic Exception

In PHP's exception hierarchy, a ValueError indicates a programming error (passing an argument of correct type but invalid value/range to a function) 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 (e.g., a user submitting a very long password). However, throwing a ValueError is appropriate here because exceeding 72 bytes in password_hash() is fundamentally a programming error for two reasons:

1. **Misuse of function parameters**: Standard end-user passwords practically never exceed 72 bytes. When string inputs to ''password_hash()'' exceed this threshold, it almost exclusively indicates that developers are improperly concatenating static pepper secrets, nonces, or secondary hashes into the password field before passing it into ''password_hash()''.
2. **Failure to account for algorithm limits**: Calling ''password_hash()'' with raw input exceeding algorithm bounds without prior validation or pre-hashing (such as raw SHA-256/SHA-512 outputs) represents a developer oversight in design rather than an unexpected end-user input state.

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

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.

Open Issues

None at present.

Future Scope

Voting Choices

Primary Vote: Requires 2/3 majority.

<poll> Deprecate passing passwords > 72 bytes to password_hash() with PASSWORD_BCRYPT in PHP 8.7 and throw a ValueError in PHP 8.8?

</poll>

Patches and Tests

References

Changelog