From dc867d96dd9ba864552ef50136b8028df80de1d8 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Wed, 5 Aug 2026 19:04:20 +0000 Subject: [PATCH 1/3] ext/standard: notice on long passwords in bcrypt Bcrypt supports passwords up to 72 characters. The remainder is ignored. Earlier proposed here: - https://news-web.php.net/php.internals/75692 - https://wiki.php.net/rfc/password_hash_spec Bcrypt truncation can result in serious security bugs: - https://pentesterlab.com/blog/freshrss-bcrypt-truncation-auth-bypass - https://www.invicti.com/blog/web-security/okta-vulnerability-bcrypt-auth --- ext/standard/password.c | 4 ++++ .../tests/password/bcrypt_72_char_limit.phpt | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 ext/standard/tests/password/bcrypt_72_char_limit.phpt diff --git a/ext/standard/password.c b/ext/standard/password.c index a28ceb7e0ced..a677cd7d85cb 100644 --- a/ext/standard/password.c +++ b/ext/standard/password.c @@ -184,6 +184,10 @@ static zend_string* php_password_bcrypt_hash(const zend_string *password, zend_a return NULL; } + if (ZSTR_LEN(password) > 72) { + zend_error(E_NOTICE, "Passwords longer than 72 characters are truncated by bcrypt"); + } + if (options && (zcost = zend_hash_str_find(options, "cost", sizeof("cost")-1)) != NULL) { cost = zval_get_long(zcost); } diff --git a/ext/standard/tests/password/bcrypt_72_char_limit.phpt b/ext/standard/tests/password/bcrypt_72_char_limit.phpt new file mode 100644 index 000000000000..6f740952ed53 --- /dev/null +++ b/ext/standard/tests/password/bcrypt_72_char_limit.phpt @@ -0,0 +1,20 @@ +--TEST-- +Test error operation of password_hash() with bcrypt hashing +--FILE-- + 4)); +var_dump(password_verify($long_pass . 'a', $hash)); +var_dump(password_verify($long_pass . 'b', $hash)); + +$hash = password_hash($long_pass . 'aa', PASSWORD_BCRYPT, array("cost" => 4)); +var_dump(password_verify($long_pass . 'aa', $hash)); + +?> +--EXPECTF-- +bool(true) +bool(false) + +Notice: Passwords longer than 72 characters are truncated by bcrypt in %s on line %d +bool(true) From 2cb7458a2e2aae5a732e82a9323168a16b88cdca Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Wed, 5 Aug 2026 19:55:39 +0000 Subject: [PATCH 2/3] Fix test title --- ext/standard/tests/password/bcrypt_72_char_limit.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/password/bcrypt_72_char_limit.phpt b/ext/standard/tests/password/bcrypt_72_char_limit.phpt index 6f740952ed53..c848c1238846 100644 --- a/ext/standard/tests/password/bcrypt_72_char_limit.phpt +++ b/ext/standard/tests/password/bcrypt_72_char_limit.phpt @@ -1,5 +1,5 @@ --TEST-- -Test error operation of password_hash() with bcrypt hashing +Test 72 character limit of bcrypt --FILE-- Date: Thu, 6 Aug 2026 07:47:43 +0000 Subject: [PATCH 3/3] Add password longer than 72 chars to test --- ext/standard/tests/password/bcrypt_72_char_limit.phpt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/standard/tests/password/bcrypt_72_char_limit.phpt b/ext/standard/tests/password/bcrypt_72_char_limit.phpt index c848c1238846..f716259e4b6a 100644 --- a/ext/standard/tests/password/bcrypt_72_char_limit.phpt +++ b/ext/standard/tests/password/bcrypt_72_char_limit.phpt @@ -11,6 +11,9 @@ var_dump(password_verify($long_pass . 'b', $hash)); $hash = password_hash($long_pass . 'aa', PASSWORD_BCRYPT, array("cost" => 4)); var_dump(password_verify($long_pass . 'aa', $hash)); +echo "This is the unexpected behavior we warn about: password is different but password_verify returns true.\n"; +var_dump(password_verify($long_pass . 'ab', $hash)); + ?> --EXPECTF-- bool(true) @@ -18,3 +21,5 @@ bool(false) Notice: Passwords longer than 72 characters are truncated by bcrypt in %s on line %d bool(true) +This is the unexpected behavior we warn about: password is different but password_verify returns true. +bool(true)