[wp-trac] [WordPress Trac] #63412: Bcrypt - Cannot Verify Password Hashes
WordPress Trac
noreply at wordpress.org
Thu May 8 03:30:43 UTC 2025
#63412: Bcrypt - Cannot Verify Password Hashes
------------------------------------+------------------------------
Reporter: aaron13223 | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: Login and Registration | Version: 6.8
Severity: normal | Resolution:
Keywords: 2nd-opinion | Focuses:
------------------------------------+------------------------------
Changes (by dd32):
* keywords: => 2nd-opinion
Comment:
Hi @aaron13223 and welcome to Trac,
Firstly, as the hash string contains `$` you do need to escape it within
PHP string literals, either as `'$2b...'` or `"\$2b..."` as noted, this is
not a bug as such, just a language construct. PHP Strings can contain `$`
without issue, it's just the PHP Parser that interpolates the variables.
Next, WordPress unfortunately requires some strings be escaped when used
for certain operations, for example: `wp_set_password( wp_slash(
"A3oc5tq9'U&d" ), $user_id );`.
`wp_slash()` is the same as `addslashes()` and would escape `A3oc5tq9'U&d`
to `A3oc5tq9\'U&d`.
This code would return truthful (Your strings from the ticket)
{{{
password_verify( "A3oc5tq9'U&d",
'$2b$10$AmZawb7AujOXxowQuhUk2.sg69pZge8uG.zdbIc.FMRmz54Op8x9u' );
}}}
However.. WordPress is effectively running:
{{{
$hash = '$2b$10$AmZawb7AujOXxowQuhUk2.sg69pZge8uG.zdbIc.FMRmz54Op8x9u';
password_verify( wp_slash( "A3oc5tq9'U&d" ), $hash );
which is:
password_verify( "A3oc5tq9\'U&d", $hash );
}}}
> We found this since we are running an import of users from another
platform which also uses bcrypt for hashing passwords. (JS bcrypt library
5.1.1 for reference)
So ultimately the issue at hand here, is that while WordPress uses bcrypt
hashes, the input password is being escaped differently between your
application and WordPress.
The Password hashes WordPress generates using bcrypt are either `$wp...`
or `$P$....` bcrypt hashes, which both contain some extra customizations,
but WordPress does support fallback to "native" bcrypt.
Those customizations affect passwords over 72 char (Which are run through
additional HMAC hashing) and no support for >4096 char passwords. See
`wp_hash_password()` and `wp_check_password()` for those:
https://github.com/WordPress/wordpress-
develop/blob/20e3fdd09fd1bbcea5e712117474d0945af528ea/src/wp-
includes/pluggable.php#L2648-L2781
I suspect you'll be able to use the `check_password` filter to alter the
behaviour, something like this is probably going to resolve it:
{{{
// Support passwords from JS bcrypt which did not slash the password.
add_filter( 'check_password', function( $check, $password, $hash ) {
if ( ! $check && str_starts_with( $hash, '$2b' ) && str_contains(
$hash, "'" ) ) {
$check = password_verify( wp_unslash( $password ), $hash );
}
return $check;
}, 10, 3 );
}}}
I don't think this is a bug per-se, but it might be worth adding back-
compat support to WordPress for this edge-case, where non-wordpress-
generated-hashes are the password and it's the non-slashed password inside
that hash.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/63412#comment:1>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list