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.
Related
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!)
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 know that there are alots of questions about this subject but i really need to ask this.
Today I've been working on encrypting passwords with md5.
So what I've done is.
I got 4 salts. (they changes depending on user values)
from email id and substr then md5 them
from email and id substr other positions
a long string, substr it and then md5 it
another long string, substr it and then md5 it
Then i md5 salt1 and 3 and the password with salt 2 and salt4
After this I have to change the password automatically whenever a user changes his email or his id getting changed.
What do you guys think about this?
Nothing.
MD5 is broken and bad.
Using the mailaddress as salt is a good idea. But using md5 is not. Use instead bcrypt, scrypt or pbkdf2.
Don't invent your own ecryption, unless you really know what you are doing, and trust me, you don't
First, let us define a few terms.
Encryption is when you encode a message so that it cannot be read. Encryption involves a plaintext, a cipher and a key. It is like putting a book (the plaintext) in a locked room (cipher), which can only be opened using a known tool (a key). There are many kinds of encryption, but that is a simple description. Encryption is two-way, meaning that you can encode and decode the message.
Cryptographic hash is when you take any kind of data and generate a fixed size value for it (usually called a hash or a digest). Cryptographic hashes are one-way, which means that you cannot reverse the process.
A salt is a unique string, or a collection of bits, similar to a nonce (a unique number that is only used once). Salts are only used to make it infeasible for a cracker to process a list of hashes. They are not supposed to be used as a secret (i.e. like a cryptographic key). The only reason people usually talk about randomness when it comes to salts is because they want to generate a unique salt (if the randomness is not great enough they may get colliding salts, for instance).
Okay, now to how you should hash a password.
A relatively safe way of hashing a password is to simply tack on a unique hash onto a password, and then save the salt with the password:
$pass = 'this is my password';
$salt = uniqid('', true);
$hash = sha1($pass . $salt);
// INSERT INTO users ('hash', 'salt') VALUES ('$hash', '$salt') WHERE ...
That is an okay way of doing it if your website does not retrieve any sensitive data from its users.
If you deal with sensitive data, or if you just want to make sure that you are doing everything you can to keep stuff safe, then there is a PHP function that does the hashing for you. It is called crypt() (read the documentation to learn how it works). Here is an example of how to hash a password using the function:
$pass = 'this is my password';
$salt = 'unique string';
$hash = crypt($password, '$2y$07$'.$salt.'$');
echo $hash;
That will securely hash a password.
The thing to realize is that the crypt() function is much more secure than anything you can come up with (unless you are a specialist in the area).
In newer versions of PHP (5.5.0+) there is a password hashing API that makes it even simpler to hash a password.
There are also various hashing libraries out there. PHPass is a popular one.
It is bad, because it uses MD5.
MD5 is a very fast operation. It can be executed billion of times per second on graphic cards hardware. It is considered bad practice to use it for any password related things.
Use bcrypt. Use a random salt. Use the upcoming PHP API for hashing, verifying and rehashing passwords. This include file implements it for versions starting with PHP 5.3.7: https://github.com/ircmaxell/password_compat
Well, "MD5 is broken and bad" is a little exagerated. Even if it can be brute-forced with a lot of CPU, it is not "broken" and is still a very useful algorithm for a lot of things involving hashing.
So "MD5 should not be used for password encryption" sounds much better to me.
When using PHP, an easy and safe option is to rely on the password_hash() (which natively generates a random salt) and password_verify() functions.
The advantage is that the encryption algorithm will transparently be updated with each new PHP version (at the moment PASSWORD_DEFAULT is set to bcrypt, but should bcrypt be "broken" it can be set to a newer algorithm), which makes any code using those functions quite resilient.
I personally do not recommend involving of the user id and his email into the hashing of his password.
You can deal with the password by:
Dynamic salt per user based on random string generated on user registration
Prepend one part of the salt and append the other around the password
Double md5: md5(md5($password))
Etc.
a simple way would be to generate a random salt for each user and hash your password like this
public function encodePassword( $raw, $salt ) {
return hash('sha256', $salt.$raw);
}
For high security hash, you can check this link which explain how to implement PBKDF2:
http://crackstation.net/hashing-security.htm#phpsourcecode
I've been looking at PHP's crypt function and a few questions on Stackoverflow, and I'm trying to figure out salted and hashed passwords.
I found this on the PHP community page:
<?php
function md5crypt($password){
// create a salt that ensures crypt creates an md5 hash
$base64_alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
.'abcdefghijklmnopqrstuvwxyz0123456789+/';
$salt='$1$';
for($i=0; $i<9; $i++){
$salt.=$base64_alphabet[rand(0,63)];
}
// return the crypt md5 password
return crypt($password,$salt.'$');
}
?>
How does something like that compare to:
<?php
// Slightly modified example from PHP community page
$password = trim(mysql_prep($_POST['password']));
// Get the hash, letting the salt be automatically generated
$hashed_password = crypt($password);
?>
Here's an excerpt from another question:
However, the PHP crypt() function can
use a variety of different hashes to
compute the hash. When you prefix your
salt with "$1$" you get a hash with an
MD5. When you prefix with $2$ you get
a crypt with blowfish, which is more
secure.
The "$1$" is prefixed to the output so
that the hash can be verified. If it
wasn't included, there would be no way
to know from the stored hash which
algorithm should be used! This
information would have to be stored
elsewhere. To save you this trouble
PHP includes the algorithm in the hash
output.
My head is spinning a bit concerning hashes, encryption and salts... but the part that really stumps me, is how do I compare a user-entered password against a salted hashed password, if the salt is randomly generated upon user creation... and not stored--and on top of that, what's the point of using crypt() with an automated salt, if you HAVE to specify the correct prefix to be able to validate the password again upon user return?
You need to store the salt with the user.
The purpose of the salt is to ensure that two users with the same password get different hashes.
This prevents rainbow table attacks.
EDIT: Your salt should contain (cryptographically-secure-)random bytes.
Right now, you're restricting it to only contain letters and numbers
The idea is to have a random salt for every user/password.
Adding a salt will just make more difficult to guess your password, but using a random salt for every account will increase the security as it'll reduce the way for an attacker to guess any other passwords, as given some current and unsecure password and/or username, the reverse engineering to guess password would be easy.
It has a big impact on cross-system security as people tends to use the name username/password on most sites.
Using a random salt makes an attack almost impossible with a dictionary as you'll need to compute every salt possible to guess any password.
If you look closely at the format of /etc/shadow, you'll see the nomenclature in the second field (with : delimiting fields):
username:${enctype}${salt}$HoPeFuLlYVerYloNGpassWOrDhAsh: ... ... ...
The salt is actually stored with the password. {enctype} is the type of encryption being employed, {salt} is the salt. Since you know the encryption type and salt, you can naturally reproduce the hash with the password they provide (thus authenticating the user).
A handy table of {enctype} values for crypt (for informational purposes):
ID | Method
─────────────────────────────────────────────────────────
1 | MD5
2a | Blowfish (not in mainline glibc; added in some
| Linux distributions)
5 | SHA-256 (since glibc 2.7)
6 | SHA-512 (since glibc 2.7)
And finally, how PHP lets you use them.
So, if you see a string like:
root:$6$foobar$JKLsdiuoilI/KSJHDKUyjh/SHDKJyUYW(....)
You know that you're dealing with SHA-512 and the salt is 'foobar' (and likely, the account is foobar too!).
It is (one) example of how salts are stored and associated with hashes. As SLaks said, don't restrict the salt to just ASCII. At the minimum you should be obtaining bytes from a PRNG or HRNG, falling back to time() only when no RNG is available.
No, You are doing it wrong. Use solardesigner's PHPASS in order to do the hashing and validation. Finally, never EVER use your own ad-hoc scheme, doing that is asking for trouble.