php password_hash, random/static salt and user auth - php

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~

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.

Related

I'm not understanding the password_hash() function

I've just learned that PHP has a password_hash() function, instead of manually calling a hashing algorithm on a password.
http://php.net/manual/en/function.password-hash.php
But I have two questions about the documentation.
The first one is about the default hashing algorithm, using PASSWORD_DEFAULT as the algorithm. Since PHP 5.5 the algorithm is bcrypt, and then it says:
Note that this constant is designed to change over time as new and
stronger algorithms are added to PHP. For that reason, the length of
the result from using this identifier can change over time. Therefore,
it is recommended to store the result in a database column that can
expand beyond 60 characters (255 characters would be a good choice)
How am I supposed to still keep users being able to log in after a hashing algorithm changes, if I'm only keeping the result of a hash, and the result of the password hash will become different?
Under the salt option it says:
Warning The salt option has been deprecated as of PHP 7.0.0. It is now
preferred to simply use the salt that is generated by default.
If the function will generate a salt, then wouldn't the resulting hash be different in two different executions for the same password? Unless the algorithm for generating salts is such that the same password would always get the same salt, but that would defeat the purpose of using salt, wouldn't it?
How am I supposed to still keep users being able to log in after a hashing algorithm changes, if I'm only keeping the result of a hash, and the result of the password hash will become different?
If the function will generate a salt, then wouldn't the resulting hash be different in two different executions for the same password?
The password_verify() function will detect the hash (and salt) used to hash a specific password and act accordingly.
Use that function to check whether the password input by a user is correct.
How am I supposed to still keep users being able to log in after a hashing algorithm changes, if I'm only keeping the result of a hash, and the result of the password hash will become different?
The password_hash documentation currently gives the following example:
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
This produces output that looks like
$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
When you call password_verify, you call it like
password_verify('rasmuslerdorf', '$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a')
Note that this does not have parameters for the algorithm or options. Why? Because they are stored in that output.
Algorithm
2y
Cost
10
Salt
.vGA1O9wmRjrwAVXD98HNO
Actual hash
gsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
In other words, password_verify does not get the algorithm from what your program is currently using. It gets it from what is stored in the database. So even if you change the algorithm, it will still use the old algorithm to verify the hash. The actual recommended way to do password verification is something like
if (password_verify($password, $hash)) {
if (password_needs_rehash($hash, $algo, $options)) {
$user->set_hash(password_hash($password, $algo, $options));
}
return true;
}
return false;
This will update the hash at login whenever it is out of date. The $user->set_hash method would save the new hash to the database (you have to implement this; the password_ functions are part of PHP). The $password is the plain text version just entered by the user. The $hash is the hash previously stored.
You can read more about this at password_needs_rehash in the PHP documentation.
If the function will generate a salt, then wouldn't the resulting hash be different in two different executions for the same password?
It would, but you don't call password_hash to verify the password. Instead, you call password_verify. The password_verify function does not generate a salt. It uses the one from the hash.
You might then ask why use a salt? This is covered extensively in other questions' answers (e.g. here), but the short version is to prevent rainbow tables. With a salt, you would have to create one rainbow table per salt. In this example, the salt is twenty-two characters long. Even if we limited salts to just decimal digits, that would be 10,000,000,000,000,000,000,000 tables. If we allow salts to be any base 64 digit, it's much bigger. Salts don't need to be secret to prevent rainbow tables.

Understanding how salt is generated/used in bcrypt password_hash

I am working on an existing Symfony 2.8 web app project that uses FOSUserBundle for user authentication.
In addition to the web front end the users can use different smartphone client to connect to the web app using a REST API. Thus the users need to be authenticated both when logging in directly in the web app and when connecting why the REST API.
Until one of the latest FOSUserBundle updates a bcrypt password hash and the used salt where stored in the database.
When connecting using the REST API, the salt is transferred to the client to locally hash the password using the same salt. The hashed password is than send back to the web app for authentication.
I know that sending the hashed password instead of plain text does not add (a lot of) additional security, since the communication is only possible using HTTPS. However this is the way the clients work: They need the salt to generate the hashed password. I can update the clients in the future, but right now this is just the way the work.
The Problem:
They way FOSUserBundle hashes the password has changed: Since it is considered to be saver to NOT specify the salt manually but to let PHP generate the salt automatically (in PHP 7 it is not even possible to manually set the salt), a manual salt is no longer supported.
This is no problem when logging into the web app directly, but since the REST clients still need a salt, this updates breaks the REST connection.
Is there any way to combine both methods? Let PHP create the salt automatically, extract and send this salt to the clients?
As far as I understand the salt is stored with the hash in the same string:
However, simply copy the 21 char salt from the hash-string and send these to the clients does not work. It seems that these 21 chars a enough to test/verify the password, but not to re-create the hash. Is this correct?
So, is there any solution to use PHP password_hash without setting a salt, and to get to know the used salt at the same time?
EDIT 1:
To answer #RiggsFolly question: MD5 was not used at any time. It is not correct, that bcryp/password_hash will not create the same hash twice. It will do so, if both the password and the salt are the same:
$s = 'password';
$salt = 'salt5678901234567890123456789012';
$options['salt'] = $salt;
$h1 = password_hash($s,PASSWORD_BCRYPT,$options);
$h2 = password_hash($s,PASSWORD_BCRYPT,$options);
echo $h1 . PHP_EOL;
echo $h2 . PHP_EOL;
Result:
$2y$10$salt56789012345678901uTWNlUnhu5K/xBrtKYTo7oDy8zMr/csu
$2y$10$salt56789012345678901uTWNlUnhu5K/xBrtKYTo7oDy8zMr/csu
password_hash will create a new hash for the same password, if the salt is not specified. This is because, the salt will be created randomly which is than of cause different on each call.
EDIT 2:
As one can see in Edit 1, using a salt with 32 chars will result in a string that only includes the first 21 chars of the salt. However this salt-prefix cannot be used to re-create the same hash since it is too short to be accepted.
However, if the prefix is filled up with 0, it seems to work:
$s = 'password';
$salt = 'salt5678901234567890123456789012';
$salt_prefix = 'salt5678901234567890100000000000';
$h1 = password_hash($s, PASSWORD_BCRYPT, array('salt' => $salt));
$h2 = password_hash($s, PASSWORD_BCRYPT, array('salt' => $salt_prefix));
echo $h1 . PHP_EOL;
echo $h2 . PHP_EOL;
So a solution could be:
let FOSUserBundle use password_hash to create the hash without manually specifying a salt.
extract the salt from the result string and pad it with 0 to a length of 32 chars
pass this salt to the client
Can anyone confirm, that this a real solution and not just some coincidence ?
You seem to misunderstand how password hashing and salts are supposed to work.
The salt is never sent to the client. It is generated (or manually specified) only once when the password is created. Its purpose is to randomize the output of the hash function, so that when the database gets into the wrong hands it is not possible to get users passwords by comparing the output to rainbow tables.
When the user uses his password to login the password is sent from the client to the server unhashed (but usually over https). The password comparing function then fetches the stored hash+password, gets the salt from it, appends the salt to the user input, calculates the hash and then compares that to the hash from the database.
Maybe the project you're on has a bad implementation of salts. In fact, one of the reasons using manual salts is discouraged is to prevent things like this.
So a solution could be:
let FOSUserBundle use password_hash to create the hash without
manually specifying a salt.
extract the salt from the result string and pad it with 0 to a length
of 32 chars
pass this salt to the client
Can anyone confirm, that this a real solution and not just some
coincidence ?
This is not a good solution. The only good way is to make sure password hashing is implemented the right way so you don't have to generate the salt more than once.
The salt is, as documented on http://us2.php.net/crypt, 22 characters and not 21.
<?php
$key = 'password';
$hash = password_hash($key, PASSWORD_BCRYPT);
$salt = substr($hash, 7, 22);
$rehash = password_hash($key, PASSWORD_BCRYPT, ['salt' => $salt]);
if ($hash == $rehash) {
echo 'ok', PHP_EOL;
}
The last 2 in the salt5678901234567890123456789012 salt changing to an u is just some magic in crypt blowfish.
Let client provide any salt. Or server gives a random salt it has never used. Treat the hash as the raw password. Re-hash the hashed password and rest stuff.

Does Bcrypt require anything to make it secure

I've been looking at encryption methods for a while now and what I've found so far is that Bcrypt is one of the best ways to do so right now. What I don't get yet is the way that Bcrypt works precisely. I understand that it takes longer to solve which is why it makes bruteforcing so hard.
But I don't understand whether it requires other measures such as a random salt to make it secure. Especially after reading about md5 and how having a random salt is almost mandatory before a hash becomes secure.
The sample code I found on php.com is this:
$options = [ 'cost' => 12, ];
echo password_hash("rasmuslerdorf", PASSWORD_BCRYPT, $options)."\n";
?>
I'm guessing the cost simply makes it so it runs through the function 12 times to encrypt the word "rasmuslerdorf". And the "PASSWORD_BCRYPT" selects the Blowfish algorithm.
Are there any big differences between PASSWORD_DEFAULT and PASSWORD_BCRYPT?
Is it enough for me to use the default function to encrypt the password on registration. And than compare the password after encrypting it that the user enters on login to the encrypted password in the database?
I'm guessing the cost simply makes it so it runs through the function 12 times to encrypt the word "rasmuslerdorf"
No, the cost parameter effects an exponential amount of work to be done.
But I don't understand whether it requires other measures such as a random salt to make it secure.
The password_hash() function automatically generates a random salt whenever you run it; alternatively, a custom salt can be passed via the options:
password_hash('bla', PASSWORD_BCRYPT, ['salt' => ...]);
By passing a custom salt you're assumed to know what you're doing. For all practical purposes you should be safe to stick with automatically generated salts.
Are there any big differences between PASSWORD_DEFAULT and PASSWORD_BCRYPT?
The PASSWORD_DEFAULT algorithm is provided to future-proof your code by always using the strongest algorithm available at that time (provided you update PHP). The notable difference is in storage requirements; whereas Bcrypt always uses 60 characters, you need to cater for bigger storage (e.g. 255 characters) for whatever will be used in the future.
And than compare the password after encrypting it that the user enters on login to the encrypted password in the database?
Please look at password_verify() for examples on how to verify the password a user enters.
The Bcrypt algorithm is the default algorithm. So, PASSWORD_DEFAULT and PASSWORD_BCRYPT are the same. The default algorithm can be configured in your php.ini file, but if you did not know that then it is most likely still the default.
The cost number is not how many times it is hashed. How many times it is hashed is calculated by using the formula, 2^cost. So, if the cost is 12 then it will be hashed 2^12 times (4096).
You do not have to think about salts when using the function. It creates the salt itself and appends it to the output hash:
$[algorithm]$[cost]$[salt 22 chars][rest is the hash]
You should never touch the hash, when using the password hashing functions. To verify a password against the has you should use password_verify().
The function you are using was made so that people can hash passwords without knowing what is happening in the background. That is a good thing, because when it comes to hashing passwords it is very easy to get it wrong, even if you think you know what you are doing.

Where is the salt stored for password_hash?

According to (relatively) new PHP documentation:
The password_hash function uses a random salt (which we should not worry about.. O_O), so if I understand correctly the salt has to be stored somewhere, else the user won't be able to login after registering to a website (different salt => different hash.)
The function documentation doesn't tell anything about interaction with a DB, and since I think storing per-user data is scalable only with a DB, where the heck does that function store the random salt? A txt file like session data?
Let's learn by example from what everyone else is telling you:
$options = [
'cost' => 11,
'salt' => 'abcdefghijklmnopqrstuv',
];
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT, $options)."\n";
Output:
$2y$11$abcdefghijklmnopqrstuu7aZVUzfW85EB4mHER81Oudv/rT.rmWm
The bolded parts are your cost and salt, respectively embedded in the resulting hash.
You can spit this back into password_verify and it will handle it accordingly:
print_r(password_verify('rasmuslerdorf', '$2y$11$abcdefghijklmnopqrstuu7aZVUzfW85EB4mHER81Oudv/rT.rmWm')); // true
The password_hash manual states
The used algorithm, cost and salt are returned as part of the hash.
Therefore, all information that's needed to verify the hash is
included in it. This allows the password_verify() function to verify
the hash without needing separate storage for the salt or algorithm
information.
Therefore the salt is already included in the hash you are saving in the db.

PHP password_hash function value isn't same for each string

I'm try to hash user password with using password_hash() PHP function.But,it function is work hashing,not constant.
<?php
echo password_hash('a',PASSWORD_BCRYPT,array(
'cost' => 12
));
?>
Result for 4th testing time
1. $2y$12$SRmipqM7AsYkx3Xc8QGHNex69rGXeVyWGTYrh9T8sh1cP3UrdjfQi
2. $2y$12$zx.GUgtcake3wMfl3/YXXeG1.8mmHKyRruL3nWj8OmA.RbEYqeW6u
3. $2y$12$XQtmFplcehkgWLbGrOUsNOlXDU/NGrwZlt3HM88hLbUHXhjXNF4km
4. $2y$12$q9/OSZdDJw7af4Hw4MGlHeY7UMtWr9/Cj0nj/N6PaoilNoUBePt7O
As some suggested to use MD5, Do not use it for password hashing.
Well now to answer your question how to check a password matches
Password_Hash() is to generate a password hash, which will create a random salt with it, this hash will be used upon hashing.
your end result would be: salt+hash, however you can give it a salt with this method in it's options but let's keep it that it does it by itselves.
Password_Verify() uses a parameter for the password and one for the hashed password.
as I said earlier the hashed password is salt+hash
which makes sense that Password_Verify() only need these and not an additional called salt
So what happens with Password_Verify() is that it takes out the salt and use Password_Hash() with that salt.
then check if the received hash equals the given hash.
if it matched then it's true, else it's false.
Password_Hash() Doc
Password_Verify() Doc
Update 18-04-2018 (d-m-Y)
WARNING
The salt option has been deprecated as of PHP 7.0.0.
It is now preferred to simply use the salt that is generated by default.
More information about Salting - SO Answer below by Veve
Why not to use MD5 for Password Hashing - PHP FaQ Answer
Why not to use MD5 for Password Hashing - SO Answer by: Silverlightfox
If you want to use password_hash() and get constants hashes (not sure of the plural...), add a salt when using it. (but don't, see the caution below)
As explained in the doc, if you don't, the salt will be randomly generated each time you use the function, and as a result the generated hash won't be constant.
<?php
echo password_hash('a',PASSWORD_BCRYPT,array(
'salt' => $salt_defined_for_the_user,
'cost' => 12,
));
?>
About the salt you should use, here is a good explaination of wmfrancia extracted from here:
SALTING
Passwords should always be salted before hashed. Salting adds a random
string to the password so similar passwords don't appear the same in
the DB. However if the salt is not unique to each user (ie: you use a
hard coded salt) than you pretty much have made your salt worthless.
Because once an attacker figures out one password salt he has the salt
for all of them.
When you create a salt make sure it is unique to the password it is
salting, then store both the completed hash and salt in your DB. What
this will do is make it so that an attacker will have to individually
crack each salt and hash before they can gain access. This means a lot
more work and time for the attacker.
Caution: you should'nt try to get constants hashes with your own salt
I'm simply responding here to your willing to have constant hashes, but as said by halfer and martinstoeckli in the comments, and also noted in the official doc,
Caution It is strongly recommended that you do not generate your own salt for this function. It will create a secure salt automatically
for you if you do not specify one.
.
You really should not create your own salt, the function tries its
best to create a safe and random one. When you store a user specific
salt as your example shows, you do the same as password_hash() does
anyway. It includes the salt in the hash-value so the function
password_verify() can pick it up from there
Mike M. has a detailed description for the use of password_verify() instead in his answer.
I think you are confused by the older methods that produce the same hash no matter how many times you try.
Eg MD5 () would five you the same hash for the same password all the time..
Password_hash gives you different string all the time as it contains a different salt each tim you run it.
When you retrieve the hash password from the database (eg based on the email the user provided)
Password_verify will will read the whole string which is salt+pass together, will use the first characters which is the salt and will use this salt with the plain password the user provided to encrypt it.
Then it will compare the result with the remaining of the salt+hash that was pulled from the database (the remaining now should be the hash.. without the salt).
I hope this helped tou understand why tou get different strings all the time tou use password hash for the same password.

Categories