1

ive been reading up about the php 5.3+ password_hash - but have a few questions, plz excuse me if im being daft. Seems 1 big catch point for making strong user password hashes is using random salts as opposed to static ones. If im checking a user logging in, surely I have to somehow have a copy of the salt used (stored in db) to check? If thats the case, do i use a secure salt function (bcrypt salt functions) and store that string in the db (and recreate with each new login) or what?

I have this:

$options = [
'cost' => 11,
'salt' => mcrypt_create_iv(22, MCRYPT_DEV_URANDOM),

];

When i echo $options['salt'] I get odd characters which i prob couldnt store in a db. Im used to the old (insecure) method of storing a random salt in the db and using that (statically) for user login auth, but the dynamic/random salt is throwing me off a bit. What am i missing? The random salt changes each time, so if i stored it now, and the user re-logged in the hash would be different so the db password wouldnt match the posted one..??

Thanks~

FstaRocka
  • 278
  • 1
  • 2
  • 15
  • 1
    The random salt has no advantages in way if the database is exposed to a third party user. Once the hash and salt are visible to an attacker, the random one is useless. You'd better use a static one, kept in your code. Statistically hacking a web server is a bit harder than a database. – Royal Bg Dec 28 '13 at 00:38
  • 1
    You don't use [password_hash()](http://www.php.net/manual/en/function.password-hash.php) to verify a user password, only to generate a salt and store the password; you use [password_verify()](http://www.php.net/manual/en/function.password-verify.php) to validate the password against that stored in the database: the password_verify() function handles checking with the salt – Mark Baker Dec 28 '13 at 00:46
  • something like this? SetEnv WEBSITE_SALT 232lhsdfjaweufha32i4fv4239tauvkjn from: http://stackoverflow.com/questions/5032341/where-is-the-best-place-to-store-the-password-salt-for-the-website – FstaRocka Dec 28 '13 at 00:47
  • @MarkBaker - i understand that, but with password_verify one has to supply a static (not random) salt to do a check of a previously stored hash. Thats the crux of my question, unless im missing something? – FstaRocka Dec 28 '13 at 00:51
  • password_verify() __doesn't__ require you to supply the salt at all, please check the manual: arguments are the password entered by the user, and the salted hashed password value from the database. It will __return__ the salt (as well as the algorithm and cost) – Mark Baker Dec 28 '13 at 00:53
  • 1
    There is no point in storing the salt separately. It is already contained in the hash returned by `password_hash()`. Just pass the entire string as 2nd parameter to [`password_verify()`](http://php.net/manual/function.password-hash.php). Btw: These functions are available since **PHP 5.5+**. – Kontrollfreak Dec 28 '13 at 00:55
  • @kontrollfreak - built into PHP since 5.5.0; but available for versions of PHP >= 5.3.7 – Mark Baker Dec 28 '13 at 00:57
  • @mark - yes im aware it doesnt require it. I have read the php article, so ur saying that password_verify using the entered password (from a form) against the previously stored hashed password (having used a previous random salt) will be able to 'see' the same password even though its using another (random salt)? – FstaRocka Dec 28 '13 at 00:57
  • Where are you getting this concept of "another" salt? You don't simply use password_verify() to check against existing passwords/salts created/stored using a different system – Mark Baker Dec 28 '13 at 01:01

1 Answers1

7

Seems 1 big catch point for making strong user password hashes is using random salts as opposed to static ones.

Random salts are the default behavior of both PHP 5.5's password_hash() and the userland implementation, password_compat.

If im checking a user logging in, surely I have to somehow have a copy of the salt used (stored in db) to check?

The salt is included in the password hash itself. There is no need to store it separately.

The random salt changes each time, so if i stored it now, and the user re-logged in the hash would be different so the db password wouldnt match the posted one..??

That's the responsibility of the password_verify() method. From the PHP docs:

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.

EDIT: Password Verification and Random Salts

I think I understand where your confusion is coming from. Hopefully this will help explain password hashing, verification, and the part played by random salts.

If you look at the source of password_compat (https://github.com/ircmaxell/password_compat/blob/master/lib/password.php), you'll see that both password_hash() and password_verify() make use of PHP's crypt() function. When you create a password with password_hash(), you store it and never pass that password through password_hash() again. The algorithm, cost, and salt are all returned with the hash. Example:

$options = array('salt' => 'ThisIsTheSaltIProvide.');
$hash = password_hash('password', PASSWORD_DEFAULT, $options);
// $hash = $2y$10$ThisIsTheSaltIProvide.EcCQwybvWB3iNxIv9FwsPJEWhR/ywZ6

We could have created the same hash by using crypt directly, which is precisely what password_hash() does behind the scenes.

$hash = crypt('password', '$2y$10$ThisIsTheSaltIProvide.'); 
// $hash = $2y$10$ThisIsTheSaltIProvide.EcCQwybvWB3iNxIv9FwsPJEWhR/ywZ6

The hash consists of:

  • Algo information: $2y$ (BLOWFISH)
  • Cost param: 10
  • An extra $
  • The salt: ThisIsTheSaltIProvide.

Using that information, password_verify() reproduces the hash and then compares it to the persisted hash, like so:

$existingHash = $2y$10$ThisIsTheSaltIProvide.EcCQwybvWB3iNxIv9FwsPJEWhR/ywZ6
$testHash = crypt('password', '$2y$10$ThisIsTheSaltIProvide.');
// if $existingHash and $testHash match, then the password is good

A new, additional salt never comes into play.

Additionally, using RANDOM salts is important. If everyone used the same salt, then users with the same password would also have the same hash. No one wants that.

Jeremy Kendall
  • 2,869
  • 17
  • 17
  • yes i know, but when a user logs in again - how do i verify the entered password against the previously stored hash if i dont have the salt/ esp if the salt is random each time? thats the crux of my confusion - – FstaRocka Dec 28 '13 at 01:11
  • @FstaRocka I added info about that to the answer. – Jeremy Kendall Dec 28 '13 at 01:12
  • 1
    @FstaRocka the salt is stored in the hash generated by `password_hash`. It's not "random each time". – lafor Dec 28 '13 at 01:13
  • @FstaRocka: I added more info to my answer in an attempt to clear up your questions about random salts and password comparisons. – Jeremy Kendall Dec 28 '13 at 03:45
  • Thanks for that. My question was using the password_hash WITHOUT the options, ie using the random salt which is generated each time. (In my original example u will see it in my code). In your example you set a fixed hash which obviously makes sense. I understand the relationship between salts and passwords, but my question originally was whether there was some magic way password_hash and password_verify could sniff out the same passwords from different salts purely from an alogorithm perspective.. or whether I needed to supply a static salt (as in the old days).. like storing it in the db. – FstaRocka Dec 28 '13 at 09:29
  • I should have been more explicit. The only reason I set a fixed salt was so it would be easy to see the relationship between the salt in the returned hash and password_verify's use of the same salt. Random or static, password_hash is only ever called one time. Every other time, for password confirmation, password verify is used. Password verify uses crypt to verify the password, not password hash, so a new random salt never enters into the equation. – Jeremy Kendall Dec 28 '13 at 10:28
  • So if a user registers, I password_hash it and store that in the db. then when they attempt to login - what do i do? how does password_verify help me when comparing to an old hashed/stored password? thats where im losing the logic - i dont see how password_verify works in this scenario - – FstaRocka Dec 28 '13 at 11:19
  • @FstaRocka I created a gist that demonstrates how `password_verify()` helps when comparing a plain text password to an old hashed/stored password: https://gist.github.com/jeremykendall/8158884 – Jeremy Kendall Dec 28 '13 at 12:21