9

if I hash the same plain-text multiple times, it returns different hash strings:

$x = 'asdf';
echo wp_hash_password( $x ).'<BR>';
echo wp_hash_password( $x ).'<BR>';
echo wp_hash_password( $x ).'<BR>';
echo wp_hash_password( $x ).'<BR>';

$P$BESNH8tVe6xfVbgXhBI2KNU.2lnyOu.
$P$BlTkcLh3PSGzXtPhofPH9ZGqisxYI0.
$P$BRyTisUAXVEhXNyTQFjOErxIgzE6GN1
$P$BI17Hnqifi2CHQsPi5z/nVbEInNjl21

So, how does wp check the password that the user enters? If it's going to hash the plaintext and check that against the stored hash, it seems the two will never match.

1
  • 1
    Try e.g. var_dump( wp_check_password( $x, wp_hash_password( $x ) ) ); to see if it isn't always true.
    – birgire
    Jul 20, 2022 at 19:28

1 Answer 1

16

By default, each time a new hash is generated (e.g. wp_hash_password()) WordPress salts it with random bytes, producing a unique hash for each call. The string returned from wp_hash_password() consists of four parts concatenated in order - the hashing algorithm ID, the exponent of hash iterations, the randomly generated salt, and finally the salted and hashed password.

For example, given the default hashing configuration, in the string $P$BI17Hnqifi2CHQsPi5z/nVbEInNjl21:

  • $P$ is the algorithm identifier
  • B indicates the number of hashing iterations
  • I17Hnqif is the random salt
  • i2CHQsPi5z/nVbEInNjl21 is the salted and hashed password

When checking a plaintext password against the hash of a stored user password, instead of simply querying the database for a matching user/password hash, WordPress retrieves the stored hash for the user and passes it into wp_check_password() alongside the plaintext password. The checking algorithm extracts the salt and iteration count from the stored hash, and uses them to generate a hash of the plaintext password before checking the two against one another.

This mechanism adds a layer of protection against rainbow table attacks as it makes precomputing/using a dictionary of hashes to passwords more expensive - instead of a single hash for a single input, in WordPress' case there are octillions of possible hashes for a single input.

A brief overview of the general technique can be found here. The source of the PasswordHash class can be scoured for the specifics as they relate to WordPress.

4
  • Interesting and informative! I notice that the "random salt", as you describe it, begins with the character "B" in all four examples. Coincidence?
    – TenMinJoe
    Jul 21, 2022 at 15:15
  • 2
    This answer is broadly correct but I think the details are a bit off. In the example, the first B indicates that the password was hashed with 2^8 iterations, and I17Hnqif is the 6-byte salt (encoded as 8 base64 characters).
    – David
    Jul 21, 2022 at 16:09
  • Not a coincidence! My initial read through the code was pretty cursory - I definitely glossed over a few things. It appears the B is actually representative of the number of iterations - I'll update the answer shortly
    – bosco
    Jul 21, 2022 at 16:10
  • Ah David beat me to it. Thank you for the insight :)
    – bosco
    Jul 21, 2022 at 16:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.