Let's hash and salt a password with:
<?php
$hash = password_hash('bonjour', PASSWORD_BCRYPT, ['cost' => 12, ]);
// no salt option mentionned
// then salt will be generated randomly, see password_hash documentation
echo $hash;
?>
The result changes on each reload of the page, that's normal : the salt is randomly generated, it can be :
$2y$12$FlxBBjTjelKkGY.SJarlL.THUZBwcl7M6V35DmZmTmYJZRwhpRkIW
$2y$12$p2pkD116hBHNc/2nyQ2WyOkrn.h8xvWvM1.Lmvsnhms2Y6zsb.j1e
$2y$12$u4ipdQQM926jfanpXnwtkupv2CH/uWoPvK563tG7p.z35GcOBOZdS
etc.
In the previous code, the salt seems to be stored nowhere (am I wrong ?). But at the end password_verify
is able to check the password with the hash,
and it works with every result, regardless what the salt was.
<?php
echo password_verify('bonjour', '$2y$12$FlxBBjTjelKkGY.SJarlL.THUZBwcl7M6V35DmZmTmYJZRwhpRkIW') ? 'yes' : 'no';
// yes
echo password_verify('bonjour', '$2y$12$p2pkD116hBHNc/2nyQ2WyOkrn.h8xvWvM1.Lmvsnhms2Y6zsb.j1e') ? 'yes' : 'no';
// yes
?>
Is it normal that password_verify
is able to check the password without having to store the salt
somewhere?
From the PHP.net manual
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.
The salt is encoded in the hash, basically.