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.
Related
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.
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.
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.
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.
I was wondering if there was a method to change the way my site hashed passwords. My coder friend wasn't the smartest when he didn't add salts to the sha512 hash. So now it is very insecure and I wish to change that. I was thinking about making some complicated code to rehash when someone who has the old hash type logs in and it would set the variable to true after adding a salt. Or I could take the currently hashed passwords and somehow fuse a salt into them. I would rather not reset my user database if I don't have to. Any idea would help. I am also quite the php noob so please explain if you include code.
It is Hashed using this method.
<?php hash('sha512',"passwordhere") ?>
Alter your user table to include a 'salt' column, default value of 'NULL'.
Alter your login code to check if the user has a salt:
If yes, compare the salted hashes and log in
If no:
Compare the unsalted hashes.
Generate a random salt.
Generate your salty hash.
Store your new salt and hash in the database.
Continue the login process.
Of course, you will also need to update your code for registration, password change/recovery, etc.
Alternatively, instead of a 'salt' column you could put in a 'hash_ver' column and use that to determine which validation method to use and when to update the hash. That way if you wish to use a hashing method that packs the salt in with the hash like bcrypt you don't get stuck trying to figure out what type of hash you're dealing with.
Every password-storing-system must have the option to switch to a better hash algorithm, your problem is not a one-time migration problem. In the answer to this question i tried to point out the necessary steps.
Note: Fast hash algorithms like SHA-* are not appropriate to hash passwords, instead switch directly to a slow key-derivation function like BCrypt. The new PHP function password_hash() will make hashing easy (it will generate a safe salt for you), and is "future proof", also it will make switching in future possible.
$old_hash = hash('sha512',"passwordhere");
$salt = ''; // Generate salt here
$new_hash = hash('sha512', $old_hash.$salt) ;