First, I see that to use CRYPT_BLOWFISH, i need to use a 16 char salt starting with $2a$. However, the php.net documentation for crypt() says that some systems don't support CRYPT_BLOWFISH. How often is that the case?
Next, from their example on the docs, I see I use crypt() as follows:
<?php
$password = crypt('mypassword'); // let the salt be automatically generated
/* You should pass the entire results of crypt() as the salt for comparing a
password, to avoid problems when different hashing algorithms are used. (As
it says above, standard DES-based password hashing uses a 2-character salt,
but MD5-based hashing uses 12.) */
if (crypt($user_input, $password) == $password) {
echo "Password verified!";
}
?>
In order to use CRYPT_BLOWFISH, would the only thing I need to modify be the first line to make it like so;
crypt('mypassword', '$2a$07$usesomesillystringforsalt$')
and then the rest of the lines are fine as is?
For PHP before 5.3.0 crypt() used the lib supplied by the OS. If you are using an earlier version, then you'd need to check your OS documentation to see if it is supported (check the value of the CRYPT_BLOWFISH constant) - if not then the algorithm is implemented within the mcrypt() extension for PHP.
The example you've quoted from the docs doesn't seem to make much sense:
$stored_password=fetch_password($user);
if (crypt($_REQUEST['password'],$stored_password)===$stored_password) {
// note that crypt automatically extracts the salt and alogrithm type
// from $stored_password
....
You only need to specify the prefix ($2a$) when creating the password.
HTH
C.
Related
Just before using password_hash(), I check if PASSWORD_DEFAULT === PASSWORD_BCRYPT to see if I need to do some cleanup on the password before it's hashed (Argon2 won't need this).
I'm simply passing it though a quick hash first, because bcrypt has an issue with NULL characters, and passwords longer than 72 characters (more info, and a different example).
But in PHP 7.4, the constant PASSWORD_DEFAULT is now set to NULL.
So how can I tell what algorithm password_hash() will use?
This is an interesting problem, for which I believe there is no standard function yet. This is not a huge problem because the hash itself contains an identifier telling us which hash algorithm was used. The important thing to note here is that PASSWORD_DEFAULT is a constant. Constants do not change.
To figure out which algorithm is used when using the default constant (which was and still is bcrypt), you need to generate some dummy hash and look at the beginning of it. We can even use a nice helper function password_get_info()
$hashInfo = password_get_info(password_hash('pass', PASSWORD_DEFAULT, [ 'cost' => 4 ] ));
echo $hashInfo['algo']; // should return either 1 or 2y
if($hashInfo['algo'] === PASSWORD_BCRYPT) {
// will be true for PHP <= 7.4
}
Edit
As of PHP 7.4.3 you can continue using PASSWORD_DEFAULT === PASSWORD_BCRYPT
https://3v4l.org/nN4Qi
You don't actually have to use password_hash twice. A better and faster way is to provide an already hashed value with Bcrypt and check it against PASSWORD_DEFAULT with
password_needs_rehash function to see if the default algo has changed or not.
bcrypt algorithm is the default as of PHP 5.5.0
So for example:
$hash = '$2y$10$ra4VedcLU8bv3jR0AlpEau3AZevkQz4Utm7F8EqUNE0Jqx0s772NG'; // Bcrypt hash
// if it doesn't need rehash then the default algo is absolutely Bcrypt
if (! password_needs_rehash($hash, PASSWORD_DEFAULT)) {
// do some clean up
}
Note: make sure that the hash value($hash) has the same cost provided in password_needs_rehash's third parameter, otherwise it will consider the hash outdated and need rehash since the cost has changed.
I'm creating a script and would ideally like to convert user password into a hash using password_hash function, however I want to make the hashing dynamic to make sure password_hash function actually exists, so i've written the following function to return a hash depending on what function is available.
function generateHash($hashpwd){
if (function_exists('password_hash')) {
return password_hash($hashpwd, PASSWORD_DEFAULT);
} elseif (function_exists('hash')) {
return hash('sha512', $hashpwd);
} else {
return md5($hashpwd);
}
}
Is there anything wrong with this method or will it work? also I am creating the hash using password_hash in the following way:
password_hash($hashpwd, PASSWORD_DEFAULT);
But most places i've seen example syntax in the following format:
password_hash($hashpwd, PASSWORD_DEFAULT)."\n";
Difference being the \n is the \n needed and what is it for?
Every version of PHP that lacks password_hash (below 5.5) is now end-of-lifed and insecure as a result. It is a) not worth accounting for these now dangerously-insecure versions of PHP, and b) not worth the security hit to your users caused by downgrading to MD5 as the hashing algorithm.
But most places i've seen example syntax in the following format:
password_hash($hashpwd, PASSWORD_DEFAULT)."\n";
I've never, ever seen this done. \n is a new line. Saving that into your database alongside the hash will cause problems - it'd only be worth doing if you were outputting the hash somewhere for debugging/viewing.
A single unsalted SHA512 is a bad fallback for password_hash. At the very least you need to use a different algorithm here including a salt. Preferably use crypt with a decent hash algorithm, which is what password_hash wraps anyway.
There's a password_hash backport available for PHP 5.3.7+, depending on your OS even some older versions. You shouldn't realistically need to support anything older than that. You need to draw the line in the sand somewhere. Most new projects require at least 5.6+. If you need to support anything older, use a trusted library like PHPass, which is backwards compatible until PHP 3.
password_hash($hashpwd, PASSWORD_DEFAULT)."\n"; is just for debugging output, the trailing newline is not part of the password hash.
You wrote:
Is there anything wrong with this method or will it work? also I am creating the hash using password_hash in the following way:
password_hash($hashpwd, PASSWORD_DEFAULT);
But most places i've seen example syntax in the following format:
password_hash($hashpwd, PASSWORD_DEFAULT)."\n";
Difference being the \n is the \n needed and what is it for?
There is a difference between both, a big one.
The second one will embed a hidden line break character that will make your password verification fail silently.
As per this user contributed note about the use of password_hash() supplied by Jay Blanchard, a member here on Stack Overflow.
Be care when using the example from the documentation which concatenates a newline character \n to the end of the hash, i.e.:
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT)."\n";
People are storing the hash with the concatenated newline and consequently password_verify() will fail.
Edit:
September 29th, 2017.
Special note:
I revisited the manual on password_hash() today and noticed that they removed the user contributed note http://php.net/manual/en/function.password-hash.php#121017 (supplied by Jay Blanchard) where the documentation was modified to remove the \n reference, where it now reads as:
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT);
which removed the ."\n" from it.
This now has a bearing on the question asked and answers given.
I believe this had something to do with a bug report filed yesterday, as per this comment in another answer inside this same question.
I also have to note that this modification (of the manual's syntax) could have adverse effects on previously posted answers stating that the url is no longer valid.
Personally, I feel they should have kept that note in there and/or stated something about its previous usage.
I am using php crypt function to make a password secure, but when I try and compare a password entered to a one in the database it will not work.
here is my code to create the password in the first place:
$crypt_password = crypt($_POST['confirm-password']);
here is me trying to compare to the password in another function:
$input_crypt_password = crypt($_POST['input-pw']);
if ($input_crypt_password == $dbpassword){
// do change password function
}
This is not working.
when i print both passwords the are different.
why are the passwords different even though I am entering the same password and using crypt function on both?
can anyone point me in the right direction?
From the docs
Example #1 crypt() examples
<?php
$hashed_password = crypt('mypassword'); // let the salt be automatically generated
/* You should pass the entire results of crypt() as the salt for comparing a
password, to avoid problems when different hashing algorithms are used. (As
it says above, standard DES-based password hashing uses a 2-character salt,
but MD5-based hashing uses 12.) */
if (hash_equals($hashed_password, crypt($user_input, $hashed_password))) {
echo "Password verified!";
}
?>
The code in the question will effectively generate a new hash every time it's called - the existing password hash needs to be passed as the salt to get a consistent result.
As also mentioned in the docs:
Use of password_hash() is encouraged.
I'd go further and say you definitely should be using password_hash instead of calling crypt for password usage (assuming php >= 5.5); in any case though for whichever whatever tools/methods you're using - please read the docs to know how to use them.
Don't use crypt directly for passwords.
If you have PHP 5.5+, than use the built in password_hash function, otherwise if you have PHP 5.3.7+ use the polyfill for this function.
Try to something like this.
$crypt_password = crypt($_POST['confirm-password'],salt);
$input_crypt_password = crypt($_POST['input-pw'],salt);
if ($input_crypt_password == $dbpassword){
// do change password function
echo "Password match successfully!";
}
Here salt parameter to base the hashing on. If not provided, the behaviour is defined by the algorithm implementation and can lead to unexpected results.
I don't know what to say that will add more detail than what everyone else has already said...
So, in modern day hash/unhashing algorithms it would be unsafe to store passwords using standard hashing functions (e.g. MD5 / SHA256) as it is quick and easy to unhash this type of entry.
password_hash() as referenced in other answers and comments should be you're #1 way to safely store passwords as it uses a one way hashing algorithm.
You should read this page!
And then in response to your original question, use hash_equals() function to compare passwords.
As many guys here said, you should use password_hash php function.
Here you can see a simple example how to use it:
<?php
$password = '123456';
$userInput = '123456';
$storedHash = password_hash($password, PASSWORD_DEFAULT);
if (password_verify($userInput, $storedHash)) {
echo 'OK';
} else {
echo 'ERROR';
}
Also as mentioned before, if you use older version of PHP, you can install polyfill.
Did you trim the input before saving in db and while making the comparison. Since the input is coming from browser this may be a reason why it is not matching. otherwise this https://stackoverflow.com/a/41141338/1748066 seems appropriate.
I modified my old post. I tried the crypt() function and now trying to work with password_hash() and password_verify() to verify the encrypted password coming from database but on each call, password_hash() function retuns a different encrypted string and password_verify() cannot match it.
This is how I am doing this.
//please ignore the syntax error if any
$data = '11';
$dbpass = password_hash($data, PASSWORD_BCRYPT);
echo $dbpass; // displays the random strings on each page refresh.
Once password is saved into database does not get match during the login process. Below is my actual function.
private function process_data($password){
$password = __STR.$password.__STR;
return password_hash($password, PASSWORD_BCRYPT);
}
private function processed($login_password, $dbpassword){
$login_password = __STR.$login_password.__STR;
return password_verify($login_password, $dbpassword);
}
On each function call for creating a hashed string for password, the function returns the different string next time.
Ok, Let's go through this one by one.
First, it's hashing, not encryption. Encryption is two-way, hashing is one way. We want to hash. We never want to encrypt. Yes, terminology matters. Please use the correct terminology.
Next, each call to password_hash is supposed to return a different hash. That's because it's generating a strong random salt. This is how it was designed, and how you really should be using it.
Further, DO NOT do the "pepper" thing of adding __STR before and after the password. You're doing nothing but potentially weakening the users password (which is not good). If you want more information around why that's a bad idea: Read This Answer.
Continuing, I would highly recommend that you do not use crypt directly. It is actually surprisingly easy to screw up and generate extremely weak hashes. This is why the password_* api was designed. crypt is a low level library, you want to use a high level library in your code. For more information on ways to screw up bcrypt, check out my blog: Seven Ways To Screw Up Bcrypt.
The Password API was designed to be a simple, one-stop shop. If it's not working for you check the following things:
Are you using PHP >= 5.5.0? Or are you using PHP >= 5.3.7 with password_compat?
Is your database column wide enough?
It needs to be at least 60 characters long.
Are you checking that the result of the function is a string, and not bool(false)?
If there is an internal error, it will return a non-string from password_hash.
Are you getting any errors?
Have you turned on error_reporting to its maximum setting (I recommend -1 to catch everything) and checked that the code isn't throwing any errors?
Are you sure you are using it correctly?
function saveUser($username, $password) {
$hash = password_hash($password, PASSWORD_BCRYPT);
// save $username and $hash to db
}
function login($username, $password) {
// fetch $hash from db
return password_verify($password, $hash);
}
Note that each one should be called only once.
Are you using PHP < 5.3.7 with password_compat? If so, this is your problem. You are using the compatability library on an unsupported version of PHP. You may get it to work (certain RedHat distributions have backported the necessary fixes), but you are using an unsupported version. Please upgrade to a reasonable release.
If all else fails, please try running this code and reporting back the output:
$hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
$test = crypt("password", $hash);
$pass = $test == $hash;
echo "Test for functionality of compat library: " . ($pass ? "Pass" : "Fail");
echo "\n";
If that returns Fail, you are running an unsupported version of PHP and should upgrade. If it returns pass, than the error is somewhere in your logic (the library is functioning fine).
The best way to store passwords is to use PHP's function password_hash(). It automatically generates a cryptographically safe salt for each password and includes it in the resulting 60-character string. You won't have to worry about the salt at all!
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
Your own scheme is very weak, first you are using MD5 which is ways too fast for generating password hashes, then you use a static salt, which defeats the purpose of a salt. Maybe you want to have a look at my tutorial about safely storing passwords.
Edit to answer updated question:
It is not necessary to add the __STR to the password (if you want to add a pepper there are better ways), but your example functions should actually work. The returned value of password_hash() will be different each time because of the random salt. This is correct, the function password_verify() is able to extract this salt for the verification. In your case the database field is probably the problem. Make sure it can hold a 60 character string.
I understand that PHP's crypt() function works like so:
crypt($password,$salt);
And that to create a bcrypt hash(which is considered very secure) the format is:
crypt("$2y$15$password",$salt);
Where $2y specifies the use of bcrypt, and 15 is the number of rounds, and it should be above 10.
Here's my question. Running:
crypt("$2y$15$password");
in my understanding, generates a nice big random salt and adds it to the hash, and when comparing passwords, this is extracted automatically. This would mean that I wouldn't have to have a salt field in my table, or generate a salt independently. Is this secure and correct?
For example, when I run:
$test = "Hello";
echo crypt("$2y$15$test") . "\n";
I get:
$6$R59d/nemygl0$/Gk6s57K2eFAkH4BWDGYhfdhbYGcqz.GRbD7qGDKOlhE5Lk.kgCoGQo/sDCCf1VDffdh7jtXPn/9rsexwrpFk1
Where the first 6 refers to some algorithm number, the next bit between the two $ is the salt, and the bit after that is the hash. Is this correct?
Also, what is the syntax for comparing this hash to another for verification?
Thanks.
I think crypt as you're using it uses SHA-512.
You probably forgot to pass $test as parameter in your crypt().
According to php docs you should pass the password as first argument (without any prefix) and then pass the salt as second argument with the $2y$15 prefix and a 22 characters salt. E.g.
crypt('rasmuslerdorf', '$2a$07$usesomesillystringforsalt$');
The function crypt() should be used like this:
$hashvalue = crypt($password, $cryptParams);
whereas the salt must be generated by yourself and is part of $cryptParams.
$bcryptAlgo = '$2y';
$cost = '$15';
$salt = '$' . functionThatGenerates22CharRandomSalt();
$cryptParams = $bcryptAlgo . $cost . $salt;
One of the difficulties is to generate a valid, unique and unpredictable salt. The best you can do is to read from the operating systems random source.
PHP 5.5 will have it's own functions password_hash() and password_verify() ready, to simplify this task. I strongly recommend to use this excellent api, there is also a compatibility pack available for earlier PHP versions. If you want to know more about how to use crypt, have a look at this article.
Your cost parameter of 15 is quite high, use it if you can afford this much of time, but today a number of 10 is ok.