I don't understand how this works
$hash = $bcrypt->hash($_POST['password']); //this string is stored in mysql
Then when a user logs in,
//get hash string from above from mysql, then
if ($bcrypt->verify($_POST['password'], $row['password'])) {
echo "Logged in.";
}
A.) Am I doing this correctly?
B.) If so, how does bcrypt remember the salt if it's not stored in the database?
The salt is prepended to the hash, and so the the function pulls the salt out of the hash from the database. This is why you have to pass the hash from the database to the verification function, instead of just rehashing the password and comparing them.
And yeah, it does look like you are doing it correctly.
Related
I have an old web application with a few users registered that is using the unsecure hash("sha256", trim($_POST["password"])) to store the hashed password in MySQL database. Now I want to update the web application to use the more secure BCRYPT password_hash() however I don't want to email all registered users alerting them to change their password. So I was thinking on implementing BCRYPT on the sha256() hashed password this way:
To save the password I will sha256() hash the user's password:
$hashed_password = password_hash(hash("sha256", trim($_POST["password"])), PASSWORD_BCRYPT);
Then I will save the BCRYPT hashed password in the database.
And to verify the user's password I would simply do this:
$hashed_password = "select hashed_password from users where email = 'abc#email.com'";
if(password_verify(hash("sha256", trim($_POST["password"])), $hashed_password))
{
echo "Welcome";
}
else
{
echo "Wrong Password!";
}
This way I will just update the user's password in the MYSQL database by looping each registered user, then I will retrieve the sha256() hashed password, and finally I will just re-save it after it has been BCRYPTed with password_hash():
$new_password = password_hash($old_sha256_hashed_password, PASSWORD_BCRYPT);
$mysql->save_user_password($new_password, $user_id);
So users will still be able to login with their old password.
What do you think about this solution?
Is it still safe even if I sha256() hash the password before BCRYPT it?
Since your current hashing system (unsalted SHA256) is indeed very unsecure, you could give immediate protection to the passwords with double hashing. As soon as possible, when the user logs in the next time, I would switch to the new algorithm and remove double hashing.
Make old hashes more secure:
$doubleHashToStoreInDb = password_hash($oldUnsaltedSha256HashFromDb, PASSWORD_DEFAULT);
Doing this for each row will protect the otherwise unsecurely stored passwords. Note the PASSWORD_DEFAULT parameter, it should be prefered over a specific algorithm, because it is future proof. And mark the double hashes, so you can distinguish between double hashes and already converted hashes, see why.
Handle new user registrations:
$hashToStoreInDb = password_hash($_POST['password'], PASSWORD_DEFAULT);
Just use the new algorithm without double hashing.
Verify logins:
if (checkIfDoubleHash($storedHash))
{
$correctPassword = password_verify(oldPasswordHash($_POST["password"]), $storedHash);
if ($correctPassword)
storeConvertedHash(password_hash($_POST['password'], PASSWORD_DEFAULT));
}
else
{
$correctPassword = password_verify($_POST['password'], $storedHash);
}
// Hashes the user password with a deprecated hashing scheme
function oldPasswordHash($password)
{
return hash("sha256", trim($password));
}
Double hashes will be converted to the new password hash function, this is possible because we have the original user password at this moment. New hashes are verified with password_verify() which is a future proof and backwards compatible function.
Adapting the password algorithm to future hardware is not a one-time task, it will be necessary as soon as new hardware will become faster. PHP offers the function password_needs_rehash() to find out whether a rehashing is necessary, then you can also calculate a new hash and store it.
Big problem with password_verify.
In my db there is a column:
password: $2y$10$1k72g4qYgd4t5koC5hj8sOit3545GfO5EhaIwVRfIiA2/eC3Hnu5e ('b')
When I want to check in order that this password is equal the letter a it given my completely 2 different codes.My code:
$hash = password_hash('b', PASSWORD_DEFAULT);
$pass = getPassword($email);
echo $hash . ", " $pass;
and it gives me:
$2y$10$oJbeQqGSee.pLcBNxqRzUecoCGc9fin7IF.evDVanN1pjmtIINSD2,
$2y$10$1k72g4qYgd4t5koC5hj8sOit3545GfO5EhaIwVRfIiA2/eC3Hnu5e
Why there are different?
It's because when not specified by the user, password_hash will generate a random salt. Salt is a string that is appended to a password before hashing. Thanks to salts being random, two users with same password will still have different hashes in database.
Good summary of the topic can be found on the wikipedia
In order to verify that the password is correct, you shouldn't manually compare hashes, which may be different with each use of password_hash because of random salts, but rather use function password_verify
You should use password_hash() before inserting into your storage and then when you verify you should use password_verify()
$pass = getPassword($email);
$verify = password_verify('b', $pass);
if ($verify) {
// passwords match
}
else {
// passwords do not match
}
See: password-verify for more info
I need to decrypt a password. The password is encrypted with password_hash function.
$password = 'examplepassword';
$crypted = password_hash($password, PASSWORD_DEFAULT);
Now, let's assume that $crypted is stored in a database (there's a "users" table, with usernames, passwords, etc) and I need to do a login: I have to see if the password entered by the user matches the encrypted password stored in the database.
This is the sql code...
$sql_script = 'select * from USERS where username="'.$username.'" and password="'.$inputpassword.'"';
...but $inputpassword is not encrypted, so it's not equal to what is stored in the password field of the table users...
So, there's a function to decrypt after the use of password_hash? Or should I change my encrypt method? Or what else?
Bcrypt is a one-way hashing algorithm, you can't decrypt hashes. Use password_verify to check whether a password matches the stored hash:
<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';
if (password_verify('rasmuslerdorf', $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
In your case, run the SQL query using only the username:
$sql_script = 'SELECT * FROM USERS WHERE username=?';
And do the password validation in PHP using a code that is similar to the example above.
The way you are constructing the query is very dangerous. If you don't parameterize the input properly, the code will be vulnerable to SQL injection attacks. See this Stack Overflow answer on how to prevent SQL injection.
The passwords cannot be decrypted as will makes a vulnerability for users. So, you can simply use password_verify() method to compare the passwords.
if(password_verify($upass, $userRow['user_pass'])){
//code for redirecting to login screen }
where, $upass is password entered by user and $userRow['user_pass'] is user_pass field in database which is encrypted by password_hash() function.
I need to decrypt a password. The password is crypted with
password_hash function.
$password = 'examplepassword';
$crypted = password_hash($password, PASSWORD_DEFAULT);
Its not clear to me if you need password_verify, or you are trying to gain unauthorized access to the application or database. Other have talked about password_verify, so here's how you could gain unauthorized access. Its what bad guys often do when they try to gain access to a system.
First, create a list of plain text passwords. A plain text list can be found in a number of places due to the massive data breaches from companies like Adobe. Sort the list and then take the top 10,000 or 100,000 or so.
Second, create a list of digested passwords. Simply encrypt or hash the password. Based on your code above, it does not look like a salt is being used (or its a fixed salt). This makes the attack very easy.
Third, for each digested password in the list, perform a select in an attempt to find a user who is using the password:
$sql_script = 'select * from USERS where password="'.$digested_password.'"'
Fourth, profit.
So, rather than picking a user and trying to reverse their password, the bad guy picks a common password and tries to find a user who is using it. Odds are on the bad guy's side...
Because the bad guy does these things, it would behove you to not let users choose common passwords. In this case, take a look at ProCheck, EnFilter or Hyppocrates (et al). They are filtering libraries that reject bad passwords. ProCheck achieves very high compression, and can digest multi-million word password lists into a 30KB data file.
Use the password_verify() function
if (password_vertify($inputpassword, $row['password'])) {
print "Logged in";
else {
print "Password Incorrect";
}
it seems someone finally has created a script to decrypt password_hash.
checkout this one: https://pastebin.com/Sn19ShVX
<?php
error_reporting(0);
# Coded by L0c4lh34rtz - IndoXploit
# \n -> linux
# \r\n -> windows
$list = explode("\n", file_get_contents($argv[1])); # change \n to \r\n if you're using windows
# ------------------- #
$hash = '$2y$10$BxO1iVD3HYjVO83NJ58VgeM4wNc7gd3gpggEV8OoHzB1dOCThBpb6'; # hash here, NB: use single quote (') , don't use double quote (")
if(isset($argv[1])) {
foreach($list as $wordlist) {
print " [+]"; print (password_verify($wordlist, $hash)) ? "$hash -> $wordlist (OK)\n" : "$hash -> $wordlist (SALAH)\n";
}
} else {
print "usage: php ".$argv[0]." wordlist.txt\n";
}
?>
I need to decrypt a password. The password is encrypted with password_hash function.
$password = 'examplepassword';
$crypted = password_hash($password, PASSWORD_DEFAULT);
Now, let's assume that $crypted is stored in a database (there's a "users" table, with usernames, passwords, etc) and I need to do a login: I have to see if the password entered by the user matches the encrypted password stored in the database.
This is the sql code...
$sql_script = 'select * from USERS where username="'.$username.'" and password="'.$inputpassword.'"';
...but $inputpassword is not encrypted, so it's not equal to what is stored in the password field of the table users...
So, there's a function to decrypt after the use of password_hash? Or should I change my encrypt method? Or what else?
Bcrypt is a one-way hashing algorithm, you can't decrypt hashes. Use password_verify to check whether a password matches the stored hash:
<?php
// See the password_hash() example to see where this came from.
$hash = '$2y$07$BCryptRequires22Chrcte/VlQH0piJtjXl.0t1XkA8pw9dMXTpOq';
if (password_verify('rasmuslerdorf', $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
In your case, run the SQL query using only the username:
$sql_script = 'SELECT * FROM USERS WHERE username=?';
And do the password validation in PHP using a code that is similar to the example above.
The way you are constructing the query is very dangerous. If you don't parameterize the input properly, the code will be vulnerable to SQL injection attacks. See this Stack Overflow answer on how to prevent SQL injection.
The passwords cannot be decrypted as will makes a vulnerability for users. So, you can simply use password_verify() method to compare the passwords.
if(password_verify($upass, $userRow['user_pass'])){
//code for redirecting to login screen }
where, $upass is password entered by user and $userRow['user_pass'] is user_pass field in database which is encrypted by password_hash() function.
I need to decrypt a password. The password is crypted with
password_hash function.
$password = 'examplepassword';
$crypted = password_hash($password, PASSWORD_DEFAULT);
Its not clear to me if you need password_verify, or you are trying to gain unauthorized access to the application or database. Other have talked about password_verify, so here's how you could gain unauthorized access. Its what bad guys often do when they try to gain access to a system.
First, create a list of plain text passwords. A plain text list can be found in a number of places due to the massive data breaches from companies like Adobe. Sort the list and then take the top 10,000 or 100,000 or so.
Second, create a list of digested passwords. Simply encrypt or hash the password. Based on your code above, it does not look like a salt is being used (or its a fixed salt). This makes the attack very easy.
Third, for each digested password in the list, perform a select in an attempt to find a user who is using the password:
$sql_script = 'select * from USERS where password="'.$digested_password.'"'
Fourth, profit.
So, rather than picking a user and trying to reverse their password, the bad guy picks a common password and tries to find a user who is using it. Odds are on the bad guy's side...
Because the bad guy does these things, it would behove you to not let users choose common passwords. In this case, take a look at ProCheck, EnFilter or Hyppocrates (et al). They are filtering libraries that reject bad passwords. ProCheck achieves very high compression, and can digest multi-million word password lists into a 30KB data file.
Use the password_verify() function
if (password_vertify($inputpassword, $row['password'])) {
print "Logged in";
else {
print "Password Incorrect";
}
it seems someone finally has created a script to decrypt password_hash.
checkout this one: https://pastebin.com/Sn19ShVX
<?php
error_reporting(0);
# Coded by L0c4lh34rtz - IndoXploit
# \n -> linux
# \r\n -> windows
$list = explode("\n", file_get_contents($argv[1])); # change \n to \r\n if you're using windows
# ------------------- #
$hash = '$2y$10$BxO1iVD3HYjVO83NJ58VgeM4wNc7gd3gpggEV8OoHzB1dOCThBpb6'; # hash here, NB: use single quote (') , don't use double quote (")
if(isset($argv[1])) {
foreach($list as $wordlist) {
print " [+]"; print (password_verify($wordlist, $hash)) ? "$hash -> $wordlist (OK)\n" : "$hash -> $wordlist (SALAH)\n";
}
} else {
print "usage: php ".$argv[0]." wordlist.txt\n";
}
?>
Like most users, I'm simply trying to figure out a secure way to store passwords. What I haven't found here (or maybe it's my lack of understanding) is how to retrieve a salted hash in my database and separate the salt from the hashed password, especially with unique salts to each password while maintaining the salt+password in a single column.
I'm finding all these cool ways to encrypt passwords (SHA-256, but does MySQL only support SHA/1 and MD5?) and other things from the PHP manual, but not sure how store and retrieve the passwords.
So, far this is all I understand:
SHA('$salt'.'$password') // My query sends the password and salt
// (Should the $salt be a hash itself?)
After that I'm lost with salts.
Retrieving the password without a salt is easy, but the salt confuses me. Where do I get the value from $salt again, especially if it's unique and secure? Do I hide them in another database? Constant (seems unsafe)?
EDIT: Is the key variable in HMAC supposed to be salt or is this something else?
First of all, your DBMS (MySQL) does not need to have any support for cryptographic hashes. You can do all of that on the PHP side, and that's also what you should do.
If you want to store salt and hash in the same column you need to concatenate them.
// the plaintext password
$password = (string) $_GET['password'];
// you'll want better RNG in reality
// make sure number is 4 chars long
$salt = str_pad((string) rand(1, 1000), 4, '0', STR_PAD_LEFT);
// you may want to use more measures here too
// concatenate hash with salt
$user_password = sha512($password . $salt) . $salt;
Now, if you want to verify a password you do:
// the plaintext password
$password = (string) $_GET['password'];
// the hash from the db
$user_password = $row['user_password'];
// extract the salt
// just cut off the last 4 chars
$salt = substr($user_password, -4);
$hash = substr($user_password, 0, -4);
// verify
if (sha512($password . $salt) == $hash) {
echo 'match';
}
You might want to take a look at phpass, which also uses this technique. It is a PHP hashing solution which uses salting amongst some other things.
You should definitely take a look at the answer to the question WolfOdrade linked to.
Personally I recommend letting MySQL do this with its built in functions.
They way I do this is to create a function in my database config file which returns a key string. The config file should be outside your sites root so that the webserver can access the file but not others. so for example:
function enc_key(){
return "aXfDs0DgssATa023GSEpxV";
}
Then in your script use it with the sql query and AES_ENCRYPT and AES_DECRYPT functions in MySQL like this:
require_once('dbconf.inc.php');
$key = enc_key();
//When creating a new user
$sql = "INSERT INTO users (username, password) VALUES ('bob', AES_ENCRYPT('{$key}', {$password}))";
//When retrieving users password
$sql = "SELECT AES_DECRYPT('{$key}', password) AS password FROM users WHERE username like 'bob'";