How do password_verify() compares password and hashed password? - php

Does anyone have an idea about how the password_verify() function works? I've searched everywhere in the net about the said function , but I never found a specific answer on how it compares its two parameters. Below is the proper syntax of the said function according to php.net:
bool password_verify ( string $password , string `$hash` )
The question is, does the function hashes the $password then compared it to $hash? or
It dehashed $hash instead then compare it with $password?

Since the entire point of a hash function is that it cannot be reversed, password_verify can't be using option 2.
That leaves option 1.
You can also look at the source code where you can see that …
zend_string *ret = php_crypt(ZSTR_VAL(password), (int)ZSTR_LEN(password), ZSTR_VAL(hash), (int)ZSTR_LEN(hash), 1);
… it crypts the password and then …
/* We're using this method instead of == in order to provide
* resistance towards timing attacks. This is a constant time
* equality check that will always check every byte of both
* values. */
for (i = 0; i < ZSTR_LEN(hash); i++) {
status |= (ZSTR_VAL(ret)[i] ^ ZSTR_VAL(hash)[i]);
}
… compares the hashed password (ret) with the hashed value passed in (hash)

There's no such thing as "dehashing". A hash is a one way function.
What password_verify actually does is, reading the salt and hashing function from the "hash", given by password_hash and then do exactly the same hashing again, with the given parameters.
Because of that, it's important to use password_verify, instead of just do something like $hash == password_hash('...'), as password_hash could use anothere hashing algorithm and creates each time a new random salt value. So calling password_hash with the same input multiple times on the same machine, would never return the same value.

Related

I can't compare password from my database and the one inputted

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.

Constant-time comparison of hash functions in PHP

I am trying to verify whether a string matches a SHA-512 (Unix) hash ($6$..) in PHP. Much like the password_verify() function for Blowfish (BCrypt) but for SHA-512 (Unix) instead.
I stumbled upon hash_equals which tends to compare two strings using the same time. However, I am unable to get the expected output boolean true on the following:
<?php
$expected = '$6$9e87b0c78da9ab83$5V16BLuWUkoG3g1oH3kwhs8rzBpjydUps1qBXuY3PkkFzDSjqklT47L5pmG8JPqDRDk.ZTJoS/ogtHkyXC2L40';
if (CRYPT_SHA512 == 1) {
$correct = crypt('OkvraMADvua', '$6$12$usesomesillystringforsalt$');
}
var_dump(hash_equals($expected, $correct));
?>
Right now, I get boolean false even though the hash value of $expected corresponds to the plaintext and the hash generated for the $correct variable also matches that same plaintext (OkvraMADvua).
The issue is that the salt is always different and that is to be expected with crypt(3) algorithms. Whenever I use the same salt, it's evident that the output would be boolean true.

Password does not match after being encrypted using crypt() and password_hash() function

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.

Encryption using crypt()

I'm currently doing a very safe login system, but I'm new to the crypt() function and need some quick assistance.
I used crypt() to encrypt the password string during signup and saved it to the database. However, how will I be able to decrypt the key during login? Or how am I supposed to do otherwise? Or would it be possibly to do some magic with the submitted password string to compare it to the encrypted key in the database?
crypt() doesn't encrypt passwords, it hashes them. The fundamental difference is, you can't get hashed passwords back (think of hash browns - if you have hash browns, you can't get the potatoes back).
So you apply the same function to the input and compare its result to the value stored in the database:
$stored_pw = get_hashed_password_from_db($_POST['username']);
crypt($_POST['password'], $stored_pw) == $stored_pw
Read the documentation on crypt() to understand the "magic" behind the code above works.
Do not encrypt the password. Instead, store it with a hash.
Popular SO thread: How should I ethically approach user password storage for later plaintext retrieval?
crypt() the provided password at login. Compare the output to the previous crypt()'s output. If they match, the passwords match.
This is the basic theory of operation of a one-way hash function.
Also this is how you should be doing it. Please note that this code is how I'd do it, and you may want to change a few of the things. And you must define your own unique salt, whether in the config file or elsehwere. It must either a) be in the global scope as I've posted, or you can change it to make it defined in the function. Also you're not encrypting, you're actually hashing. Encryption is both ways, hashing is encryption in one way. Meaning you cannot decrypt a hash. You can only bruteforce guess the original plain text.
/*
* Copyright (c) 2012, Macarthur Inbody
* The following code was posted on http://stackoverflow.com/questions/8195689/encryption-using-crypt
* The license is simply CC-by https://creativecommons.org/licenses/by/3.0/
*
*
*
*/
/*
*
* This is used to hash their password.
*
* #param $password string the users supplied password
* #param $username string the users supplied username
* #param $rand_salt int the secondary salt -2^31-1 to 2^31-1 Must be defined previously.
* #return string the hashed password
*/
function hash_pass($username,$password,$rand_salt){
global $unique_salt;
$main_salt=base64_encode(hash('sha512',$username.$password.$config_salt);
$main_salt=str_replace('+', '.', $salt);
$main_salt=str_replace('=','/',$salt);
$main_salt='$2$06'.$main_salt; //change this here to the cost factor that you want
$hashed=crypt($unique_salt.$username.$password.$rand_salt,$main_salt);
return $hashed;
}
function gen_rand_salt(){
return rand();
}
function rand_str($length,$additional_entropy){
$max_length=ceil($length/28);
if(!is_defined($additional_entropy)){
$additional_entropy='';
}
$str='';
for($i=0;$i<=$max_length;++$i){
$str.=base64_encode(sha1($i.''.microtime().$additional_entropy,true));
}
$str=substr($str,0,$length);
return $str;
}
/*
*
* Generate A temp password/token
*
* This function generates a temporary password and also gives you
* the hashed password too. It is an array, arr[0]=password, arr[1]=
* hashed password. If it fails it'll return -1;
*
* #param $username the username
* #param $rand_salt the random salt value, must be given.
*
* #return array if it is successful array, if it fails it's a number of -1
*/
function generate_temp_password($username,$rand_salt){
global $unique_salt;
if(!is_defined($rand_salt)){
return -1;
}
$pass_len=12; // change this to what you want for password recovery
$pass_arr=Array();
$password=rand_str($pass_len,$unique_salt.rand().$rand_salt);
$password=substr(base64_encode(sha1($rand_str.$rand_salt,true)),0,$pass_len);
$hashed_password=hash_pass($username,$password,$rand_salt);
$pass_arr[0]=$password;
$pass_arr[1]=$hashed_password;
return $pass_arr;
}
As stated in teh code, the license is CC-By as I figure it's good enough for most things. Also please keep the block the same as far as the link to this page as that's what I do with all of my own source code. Also I realize the "random" string isn't really that random, but it's random enough to be useable by you for the purpose that it is going to be.
Edit 2:Also be sure to escape the user's username. I'm not escaping the password, since I'm hashing it, thus escaping it isn't necessary since it's already mitigated and would just waste cycles. But only if you're doing something like this. Make sure to escape the username with mysql_real_escape_string. If you're using php5+ you should look into mysqli(if you're using mysql). If you're using another system, then you'll have to look it up yourself as I only know mysql. I'm going to be away for a couple of days, so I really hope this works for you. I will check it from time to time, but I may forget... so yeah. I hope this helps you, as it's safe, secure, and should work fine for you.
Edit 3: Changed the random string function to make it a bit stronger since I forgot that this is going to be use to generate temporary passwords. That should make it random enough to be used for this purpose since otherwise the generated password might be able to be known by someone knowing the exact time(with the current microtime) though highly unlikely, this still makes it a bit stronger and should make it safe from those kinds of attacks. It should not be completely production ready, and should be secure for your system. Just make sure to set the $unique_salt variable somwhere in the global scope, or set it in every time that it's used in each of those functions.
See
http://php.net/manual/en/function.password-hash.php
and
http://php.net/manual/en/function.password-verify.php
Also never use rand() if you need safe random values. It's the worst source for random values in PHP.
In PHP 7 you should use
http://php.net/manual/en/function.random-bytes.php
instead. For earlier versions see
http://php.net/manual/en/function.openssl-random-pseudo-bytes.php

Confusing PHP BCrypt implementation

I'm trying to find a hashing algorithm to use to save my passwords in the DB (along with a salt). I found this on SO: How do you use bcrypt for hashing passwords in PHP?. The top answer has a library that seems to work. I'm just a bit confused by the verify method.
To run the script, the author of the library provides:
$bcrypt = new Bcrypt(15);
$hash = $bcrypt->hash('password');
$isGood = $bcrypt->verify('password', $hash);
The hash method takes the input string and hashes it using mcrypt + a salt. The result is returned to $hash. Then the verify method takes the same input as hash method + $hash. verify then calls mcrypt + the value of $hash (instead of some random salt) -- and somehow the results are the same $hash???
Simplified:
hash = password + salt
verify = password + hash
verify = hash <-- huh???
What did I miss?
ALSO:
I plan to modify the code such that hash method accepts a $salt parameter. I will make getSalt a public method. This way, I can get a salt, store it in the DB, store the hash in the DB, and use user's input + salt to see if it hashes to what I have stored in the DB. Any problems with this idea?
I'm not familiar with the actual bcrypt, but something along the following lines must be what happens:
When you create the "hash", $bcrypt->hash('password') returns a string that contains both the hash value and the initial salt.
When you subsequently say $bcrypt->verify('password', $mystring), the function first reads the salt part out of your input string, then creates the hash again with that salt, and finally compares that computed hash with the hash part of your input string.
In any salinated hashing scheme, you will always have to store the salt along with the hash value.
The hash in this case also contains the salt. It's concatenated to the beginning of the string, usually as something like $2a$12$SALT.... Passing the hash to verify is just done to use that salt value again, the rest of the hash is ignored. That also means you do not need to modify the function for use with salts, that's already part of the algorithm.

Categories