Why does PHP’s password_hash() output change each time the same password is hashed?

By | March 22, 2019

Nota bene: the hash() algorithm in this article has been slightly altered so that the code below doesn’t work. This is intentional: this code should not be used for secure hashing as it is merely a demonstration of why the same password can generate a different hash.

The hash for a password should change each time the same password is hashed. I’ll explain using simple hashing and add steps to show how and why it changes.

Take a simple password like “mypassword1”. We can make a simple hash from it using just hash().

Output:

This creates a hash, which is usually a string that basically summarizes data then outputs it. This can mix up the data so that doesn’t make sense to humans, like “4b492f”. It always mixes the data up in the same way, so the output won’t change unless the input changes.

Notice how these to hashings produce the same output:

Output:

A problem of having the same result for the same input is that once a hacker finds the matching hash for your password, they not only know your password, but can share it, sell it, or even log into your account. Passwords, and entire databases, can also get stolen.

The hash above has has already been cracked and added to a table of cracked passwords, as have many others. We need to find one that hasn’t been cracked. Using a complicated password helps, but another solution is to use a more complicated hashing function. If we add words to the hash, we will get a different result.

Output:

This is a good start to securing a password, but not nearly good enough. The appended characters are known as a pepper. Since the hash has changed, it might no longer be part of pre-hacked table that hackers send around, but there’s still a problem: the same pepper is being used for every password.

Here’s an example of two different passwords using the same pepper:

Output:

While it’s great that their hashes are different, the pepper is the same. If a hacker finds out the pepper, it makes cracking hashes easier. If they get access to a database that uses the same pepper on millions of hashes, they’ll all be easier to crack, because cracking usually involves hashing millions of possible password combinations until a match is found in the database. If they know the pepper, they need fewer guesses. If they try “mickeyhelpme” then they won’t get a match, but if they try “mickeymypassword1” they’ll get a match and they’ll know your password.

Unlike a pepper, a salt is a random word or string of characters that are usually generated randomly. This solves the pepper problem just mentioned. Having different salts results in different hashes on the same data:

First, let’s make a simple salt. We’ll just use integers from 1,000 to 9999 so that the salt is always four digits long. This simplifies things later on.

Output:

We can append this to the password as our random salt.

Output:

Notice that the hash has changed yet again now that a random salt has been applied. There’s still a problem, however. A powerful computer can still crack this password by hashing many different passwords and seeing if that hash matches your hash. If it does, they know your password. We can exploit the fact that computers must try many attempts at guessing a password by making each attempt costly. When there are millions of attempt’s, just adding a few microseconds adds up to hours of extra time to crack just one password! A simple and effective method to add work, also called “difficulty” or “cost,” is to simply rehash the hash many times.

We’ll rehash the password 50 times:

Output:

Notice that we repeatedly pass the output hash back into the hash loop, changing it every time. Cracking this hash forces a hacker to do more work, but we still have the same problem of consistency: we’re using the same number of rehashes. We can randomize this too.

We can force any hacker to use between 100 and 999 iterations of the hashing. Limiting the range to 100 to 999 results in a value that’s three digits long.

Output:

The password is far better hashed now. But there is one more problem: the purpose of storing a password hash is to check if a password you enter is valid. This is done by entering a password when you log in, hashing it, and checking if it matches the stored hash. If they match, then your password is verified. But how does the hashing routine know how many iterations the password was originally hashed, or what random salt was used? You can actually make these public. The goal isn’t to hide this information, but to make the hash itself as random as possible to avoid a match during cracking. We can append the salt and iterations right to the hash. If the database gets lost or stolen by hackers, it doesn’t make any difference if the iterations and salt are public or not. In fact, other security methods make parts of the password public, usually in the form of a key.

I’ll refer to the iterations and salt parts of the hash as “segments.” I’ll delimit each segment with a hyphen. This allows for the following code to create a hash:

Output:

The hash() method used above contains an algorithm that creates the hashed ouput. But what if one day it’s cracked? We need a backup plan. We can simply use other algorithms for hashing. There are plenty of hashing algorithms available. We could make “1” represent the current hash() algorithm, “2” represent the sha256() algorithm, and “3” represent “bcrypt () We can prepend this code to the hash above as another segment:

Lastly, we can make two functions: one to create the hash from the password and another to compare hashes. Again, the first segment is the hashing algorithm used, the second is the iterations, and the third is the salt. Combined with the hyphens, the output will always be 42 characters.

Output:

In reality, a salt would have 16 or more random bytes, there would be far more iterations, and different hashing algorithms would create a hash of different lengths. I also cheated in the code and ignored checking the hashing algorithm used to hash. Fortunately, PHP has taken care of password creation and verifying (matching)with just two simple functions.

PHP’s password_hash() is similar to the method above. By using random parameters, a password will change each time it’s hashed despite using the same password. password_hash() is currently the favored method for hashing passwords, hopefully through a secure socket. As with the method above, you don’t need to generate your own salt or cost, and the algorithm can change at any time. password_verify() tries to match a password against its hash. Since the parameters are obtained from the hash, the only inputs you need is the password and the stored hash.

Share this article

Leave a Reply

Your email address will not be published. Required fields are marked *