PHP password_hash Check Two Hashes

3.9k views Asked by At

If I have a two password hashes created using password_hash function, how can I tell if they came from the same base password? I know it uses a different salt each time. I do not have the plain text.

For instance: $2y$10$M6CnjqaxuUKNhg84T8NpLeylkUrvP1pzoZNhBWfpSzP2zJneuS1re and $2y$10$ZSlQNIbsLWfj7JLCSkvFLeS/adH.KnGZTgA1BcvyPXl7BEn7GhREO both came from hashing test. How can I write a function that will return true if given these two hashes as parameters?

Is this possible?

So:

<?php
function check_hashes($hash1, $hash2) {
(some code)
}
echo strval(check_hashes('$2y$10$M6CnjqaxuUKNhg84T8NpLeylkUrvP1pzoZNhBWfpSzP2zJneuS1re', '$2y$10$ZSlQNIbsLWfj7JLCSkvFLeS/adH.KnGZTgA1BcvyPXl7BEn7GhREO'));
echo strval(check_hashes('$2y$10$ZSlQNIbsLWfj7JLCSkvFLeS/adH.KnGZTgA1BcvyPXl7BEn7GhREO', '$2y$10$LoUOu3kt7zm1YZI1PtAsD.yzWF0b9jqOaAH64lK51VtgqRJZBgtO6'));
?>

Would output:

TRUE
FALSE
2

There are 2 answers

4
Stoic On BEST ANSWER

No, you can not check if the two hashes belong to the same password, unless you already know the password. If you could, this basically fails the purpose of using the password_hash function.

If you know the password, you can simply, use the method: password_verify to verify your passwords:

$res1 = "$2y$10$ZSlQNIbsLWfj7JLCSkvFLeS/adH.KnGZTgA1BcvyPXl7BEn7GhREO";
$res2 = "$2y$10$M6CnjqaxuUKNhg84T8NpLeylkUrvP1pzoZNhBWfpSzP2zJneuS1re";
var_dump(password_verify("test", $res1)); // returns true
var_dump(password_verify("test", $res2)); // returns true

So, as you can see, both the hashes will give you true with password_verify() method, and therefore, you do not need to check if the two hashes belong to the same password or not. And, if you do, the above logic can be used to construct a function like this:

function belongs_to_password() {
  $args = func_get_args();
  $str  = array_shift($args);
  foreach ($args as $hash) {
    if (!password_verify($str, $hash)) return false;
  }
  return true;
}

The above function can be used to check whether the hashes passed to this function are all for the same password or not. And, can be used like this:

$res1 = "$2y$10$ZSlQNIbsLWfj7JLCSkvFLeS/adH.KnGZTgA1BcvyPXl7BEn7GhREO";
$res2 = "$2y$10$M6CnjqaxuUKNhg84T8NpLeylkUrvP1pzoZNhBWfpSzP2zJneuS1re";
var_dump(belong_to_password("test", $res1, $res2)); // returns true
2
Mike Brant On

The information about the salt and hash methodology is encoded into the hash. This allow password_verify() to work without separate knowledge of the salt (i.e. you don't need to store the salt in a database somewhere).

Note this section from pasword_verify() documentation:

Note that password_hash() returns the algorithm, cost and salt as part of the returned hash. Therefore, all information that's needed to verify the hash is included in it. This allows the verify function to verify the hash without needing separate storage for the salt or algorithm information.

As such, you can simply run password_verify() against both hashes.

$hash1_verified = password_verify('test', $hash1);
$hash2_verified = password_verify('test', $hash2);

If both of these return true, they are both hashes of test.

A function for comparing a base word against an arbitrarily-sized array of hashes to verify all hashes come from that same base word might look like this:

function hash_base_matches_all_hashes($base, $hash_array) {
    for($i = 0; $i < count($hash_array); $i++) {
        if (false === password_verify($base, $hash_array[$i]) {
            return false;
        }
    }
    return true;    
}