password_verify and password_hash in php - php

I know that password_verify can be used to check a password's validity against a pre-stored hash. I'm also aware that password_hash requires us to choose a hashing algorithm such as PASSWORD_ACTUAL or PASSWORD_BCRYPT.
When password_verify($pass, $hash) converts $pass into hash how will it know which algorithm to use?

It can tell because the hash itself has the required information.
Per PHP 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.
Reference
For example, a PASSWORD_BCRYPT hash would look like this:
$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
|-----|----------------------|------------------------------|
A1,A2 B C
A1: 2a indicates the hash algorithm. For Bcrypt it's either 2a, 2b, or 2y
A2: 10 is the cost parameter of the hash. This makes the hash safe against timing attacks, since you can just increase this number to increase the "work" it's needed to compute
B: this is the 128-bit salt
C: the resulting 184-bit hash
With all those parameters (for now, just looking at a1) it can already tell what algorithm it is.

Hashes created using PASSWORD_BCRYPT have an identifier at the beginning: $2y$ and also makes sure the length of the hash is 60 characters. password_verify() most likely looks at one or both of these to determine what kind of hash to use.

Related

Why is PHP's hashing Algo not working right?

I use PHP's PASSWORD_DEFAULT as the hashing Algorithm; now, I noticed that, if I use it with a salt, only the first 8 chars are verified.
Here is a bit of code I wrote to test that:
<?php
$test_pw = "%ImAVery1234Secure!Password$";
$test_pw_to_be_hashed = "%ImAVery";
//
$salt = bin2hex(openssl_random_pseudo_bytes(32));
$password = $salt.$test_pw;
$password_hashed = password_hash($salt.$test_pw_to_be_hashed, PASSWORD_DEFAULT);
echo password_verify($password, $password_hashed);
?>
This returns 1 for me. If I remove one more chars from the test_pw_to_be_hashed value, it returns 0.
Is there any way to hash the whole password? Do I have to use another hashing algorithm?
If I have to use another hashing algorithm, is there any way to check it with PHP's password_verify method, or do I have to "re-hash" it and then just check both values like below?
if(password_hash($salt.$test_pw_to_be_hashed, OTHER_ALGO) == $db_password)
If I have to change the hashing algorithm, is there any way to re-hash the passwords used currently, or do I have to hash them again when I have the plain text password (when a user logs in again)?
The built-in function password_hash already generates a random hash for you. It returns a string containing the algorithm identifier, the salt and the cryptographic hash. This complete string is confusingly called "hash" itself in the PHP docs. In fact it is a composition containing the hash.
The function password_verify can identify the hash algorithm, the salt and the hash from the string generated by password_hash.
$hash_from_db = '$2y$10$1Ow3T9597X1e9W8dtVbKK.VAAo6Op6xIbglp.3amRCSVgLlTevhjS';
$test_pw = '%ImAVery1234Secure!Password$';
// this gives another hash each time due to a random salt
echo password_hash($test_pw, PASSWORD_DEFAULT), PHP_EOL;
// this verifies against a strored hash
echo password_verify($test_pw, $hash_from_db) ? 'correct' : 'incorrect';
A cryptographic oneway hash function is meant to be irreversible by design. Thus there is no way to rehash older hashes directly. You have to wait until the next login. Then you can check whether the hash in the database is compliant to the current security standard. Older algorithms will still work. First check as usual, whether the provided password is correct. Then recreate a new hash from the given plain password.
If you for some reason do want an additional own long salt string, you have to store that along with the hash as well. When verifying, you need to use that same salt with the user provided password in the same way as you have built the hash input before and pass the combined string to the password argument of the password_verify function.
Since some crypto algorithm might limit the length of the password input, it is a good idea to append further salt strings to the end of the password rather than prepending. Otherwise in the worst case the verification would always be true when the input is truncated to a shorter length than the length of a prepended salt.
As stated in the password_hash PHP docs
Caution
Using the PASSWORD_BCRYPT as the algorithm, will result in the password parameter being truncated to a maximum length of 72 bytes.
bcrypt is the current default algorithm used by password_hash. Thus prepending instead of appending a longer salt would counteract the security.
Though building a longer hash from the generated hash by an own implementation would be possible, cryptography is a complex sciency and custom implementations will most likely introduce more security holes (e.g. timing attacks) rather than increasing security. Use the options provided by PHP's implementations instead, e.g. adjust the cost of the calculation as documented in Predefined Constants. A calculation time of about 3-6 seconds is a fair compromise. This is usually done only once per session and the session is secured by a less secure session id. Consider to reask the password when accessing sensitive data like password change, critical personal information aso.
Keep in mind that even a strong password hash algorithm is considered as not secure enough. Consider to implement multi factor authentication, e.g. sending a TAN to a mail address or mobile phone or even better supporting a cryptographic hardware dongle. (This does not replace but extend password security!)

How does the hash match when the salt is generated randomly in password_hash?

How does this hashing decryption works when the salt is generated randomly by default. In my perceptions it seems something like this:
password_hash(random_salt1+pw) != password_hash(random_salt2+pw)
How does password_verify(random_salt2+pw) knows the original salt to decode when it's generated randomly?
Thank you for reading this.
It reruns the hash routine on the new text (password) using the parameters it placed in the hash and if that matches the old hash BINGO.
If you look at the output from password_hash() all the parameters are there in the resulting hash
See example
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
echo password_hash("rasmuslerdorf", PASSWORD_ARGON2I);
echo password_hash("rasmuslerdorf", PASSWORD_ARGON2ID);
RESULTS
$2y$10$nbX83VUlyVstPCckavcJy.wQ84i8/cmBD/oeDV/zWrHXkuG6t/9fy
$argon2i$v=19$m=65536,t=4,p=1$QlQ4emNEb1UxR1JiTG5Ddw$vw4HeiM9CEo8c2KNUslpC7qpH9M9Lo+WxBhX0UPp4oo
$argon2id$v=19$m=65536,t=4,p=1$U1loZThCYWtXcnpYWWV3NA$52eO0Ig9a1/pwqK3NPeNxwQpRuml36pjN2UN5BaGVGo
Notice that even if you use the same password (text string) you will not get the same hash from password_hash(), this is because the salt is randomly generated as part of the hashing process.
Also password_hash() explicitly says DONT ADD YOUR OWN HASH. It generates a strong hash internally. A much better one that you are likely to create for yourself
From the manual of all places 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.
Also from the manual Warning The salt option is deprecated. It is now preferred to simply use the salt that is generated by default. As of PHP 8.0.0, an explicitly given salt is ignored.
See this for more information about how the hash is made up

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.

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.

Switching from md5() to crypt()

So far I have been using md5 to hash passwords on my site, no salt.
Now I am building an application that will have to be more secure and I'm reading md5 can be easily brute-force attacked.
So I want to use crypt() to hash the passwords.
What I have not fully understood is:
Do I have to provide a salt or is the built-in generated one ok?
How many times (if more than one) should I iterate the crypt function to be safe?
With md5, no matter the length of the input string, the hash was 32-digit. Does crypt return a standard length of hashes too?
You need to provide a salt, if you want to specify encryption other than DES. Otherwise, you're good with the default salt.
You don't iterate the crypt function yourself, this is done internally with algorithms where it makes sense. Number of iterations is specified via the salt.
Yes, the hash length of a given hash algorithm is standard; different hash algorithms have different hash lengths, however.
crypt can use different hash algorytms. With md5 it returns 128 bit integer (with 32 chars hex representation). Using crypt with a salt once is safe enought. It's recommended the salt to be provided by the application
An optional salt string to base the hashing on. If not provided, the
behaviour is defined by the algorithm implementation and can lead to
unexpected results.

Categories