Why does hashing a password result in different hashes, each time?

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.