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
Related
I hashed my password into the database using the crypt() function.
$cryptpass = crypt($user_pass);
Now when i try to login in with my password that is "test" it wont work.
Here is the PHP from login
$user_name = mysql_real_escape_string($_POST['user_name']);
$user_pass = crypt($_POST['user_pass']);
$user_level = mysql_real_escape_string($_POST['user_level']);
$encrypt = md5($user_pass);
$admin_query = "select * from admin_login where user_name='$user_name' AND user_pass='$user_pass' AND user_level='$user_level'";
Sorry i am kinda new to password hashing , in the whole time i saved my passwords as plain texts.
EDIT: When i echo the query here are the results
crypt = $1$vh4.Mq4.$YaABh9aqRKbKpACTDApWb1 ,select * from admin_login where user_name='testcr' AND user_pass='$1$vh4.Mq4.$YaABh9aqRKbKpACTDApWb1' AND user_level='a' ,the real password is "test" .
You have choosen an extremely unsafe way to store the passwords (DES and MD5 hash without salting). You should think about using PHP's function password_hash(), to create a BCrypt hash.
For verification you will first have to get the hash from the database by username, afterwards you can verify the password with password_verify(). Directly verifying the hash with an SQL query is not possible because of the salt.
// 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);
If you are interested to read more about this topic, have a look at my tutorial about safely storing passwords.
Try debugging your SQL statement.
$admin_query = "select * from admin_login where user_name='$user_name' AND user_pass='$user_pass' AND user_level='$user_level'";
echo $admin_query;
Run the query in your SQL engine and see if you can spot the differences.
As part of learning php, I wanted to try a register and login page, however, the site I'm following for how to store a password uses MySQLI and I'm not using that:
Hashing the password
$password1 = 'hello123';
// A higher "cost" is more secure but consumes more processing power
$cost = 10;
// Create a random salt
$salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
// Prefix information about the hash so PHP knows how to verify it later.
// "$2a$" Means we're using the Blowfish algorithm. The following two digits are the cost parameter.
$salt = sprintf("$2a$%02d$", $cost) . $salt;
// Value:
// $2a$10$eImiTXuWVxfM37uY4JANjQ==
// Hash the password with the salt
$hash = crypt($password1, $salt);
I'm stuck on retrieving the password however, here's the site's code for it:
$username = 'Admin';
$password = 'gf45_gdf#4hg';
$sth = $dbh->prepare('
SELECT
hash
FROM users
WHERE
username = :username
LIMIT 1
');
$sth->bindParam(':username', $username);
$sth->execute();
$user = $sth->fetch(PDO::FETCH_OBJ);
// Hashing the password with its hash as the salt returns the same hash
if ( crypt($password, $user->hash) === $user->hash ) {
// Ok!
}
From what I can see, he grabs the hash value of the password for the user in the DB and compares the password that was passed using the hash and check with the one in the DB.
I've been trying this but the result hash is never the same as the original one:
$pwdtocheck = 'hello123';
// no call do DB yet, doing this on the same page after hashing, the $hash is the same as above
$pwdhash = crypt($pwdtocheck, $hash);
// if I echo $pwdhash it's never exactly the same as the $hash.
if ( $pwdhash === $hash) {
echo "same pwd";
}
I cannot see the actual problem in your code, maybe your database field is smaller than 60 characters, or you are comparing different passwords. In every case there is an easier and safer way to hash passwords, just use the new functions password_hash() and password_verify(). There exists also a compatibility pack for earlier PHP versions.
// 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);
EDIT:
I see now that you are using a salt when you compare the passwords. In your line:
$pwdhash = crypt($pwdtocheck, $hash);
the $hash variable has the salt prepended to it because crypt() will automatically do that for you. crypt() will extract the salt from the $hash because it knows the expected length of the salt based on the algorithm used. See the documentation.
I'll keep my original answer below for context and for those looking for a similar answer.
END EDIT
The password is not the same for you because you are using a salt when you originally hash the password to put in your database, but you are not salting the password later when you check against the database.
You should use the same salt string when you save the password as when you check the user's password on login. Usually, you will randomly generate the salt string for each password (as you are doing) and then save the salt string to the database along with the hashed password (either in the same column or its own column) so that you can use the same salt to check the user's password on login.
See https://crackstation.net/hashing-security.htm#salt for reference.
I can't access the article you're referencing but I imagine:
You need to check using the salt as the salt and not the hash.
crypt($pwdtocheck, $user->salt) == $user->hash
should work
I'm using SHA-512 to encrypt passwords. I think that I'm doing everything correctly (and even duplicating code that is successful elsewhere), but every time I try to enter a password, it fails to match what I have stored in my database.
$sql = "SELECT hashed_password FROM Users WHERE user_name='$username'";
$result = mysql_query($sql);
$db_password = mysql_result($result, 0);
if (crypt($currentPlaintext, $db_password) != $db_password) {
echo "Your current password is incorrect.";
die();
}
I have verified the following obvious potential problems and eliminated them as causes:
1) There is an encrypted password stored in my database
2) I am able to retrieve this password and store it in the variable $db_password
3) The variables $username and $currentPlaintext do have the correct values as passed over by my ajax function call.
So, why isn't this working? Am I missing something obvious? Thanks!
EDIT: Thanks for the comments so far. To clarify, as I understand the crypt() function, the second argument of crypt is actually the entire string that is stored in the db. This string includes not only the hashed password, but also the algorithm, salt value, and number of rounds of hashing. The crypt() function is supposed to pull the salt value out of this string and then apply it to hash the first argument. The result is then compared to the previously hashed password. That's why this is so confusing. I don't have to give it a salt value - the salt is there in the database. Am I misunderstanding what is going on in this function?
I originally hashed the password with this code:
$salt = uniqid();
$algo = '6';
$rounds = '5000';
$cryptSalt = '$' . $algo . '$rounds=' . $rounds . '$' . $salt;
$hashedPassword = crypt($plaintext, $cryptSalt);
Are you sure you are using Sha512? Depending on the system crypt() might use a different algorithm.
You check if
crypt($plaintext, $pass) == $pass
Looking at the crypt manual you see that the second argument of crypt is $salt. So you need to give it the salt you used when you created the hash. If you do not use a salt (which you should be doing), then try to remove this second argument.
The crypt function in PHP doesn't necessarily return an SHA512 hash.
Use hash('sha512', $currentPlaintext) instead.
Probably a very newbie question but, Ive been reading around and have found some difficulty in understanding the creation and storage of passwords. From what i've read md5/hash passwords are the best ways to store them in a database. However, how would I go about creating those passwords in the first place?
So say I have a login page with user bob, and password bob123
- how will I
1. get bobs password into the database to begin with (hashed)
2. how do I retrive and confirm the hashed password?
Thanks
Edit 2017/11/09: Be sure to take a look at the answer from O Jones.
First off MD5 isn't the greatest hashing method you could use for this try sha256 or sha512
That said lets use hash('sha256') instead of md5() to represent the hashing part of the process.
When you first create a username and password you will hash the raw password with some salt (some random extra characters added to each password to make them longer/stronger).
Might look something like this coming in from the create user form:
$escapedName = mysql_real_escape_string($_POST['name']); # use whatever escaping function your db requires this is very important.
$escapedPW = mysql_real_escape_string($_POST['password']);
# generate a random salt to use for this account
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
$saltedPW = $escapedPW . $salt;
$hashedPW = hash('sha256', $saltedPW);
$query = "insert into user (name, password, salt) values ('$escapedName', '$hashedPW', '$salt'); ";
Then on login it'll look something like this:
$escapedName = mysql_real_escape_string($_POST['name']);
$escapedPW = mysql_real_escape_string($_POST['password']);
$saltQuery = "select salt from user where name = '$escapedName';";
$result = mysql_query($saltQuery);
# you'll want some error handling in production code :)
# see http://php.net/manual/en/function.mysql-query.php Example #2 for the general error handling template
$row = mysql_fetch_assoc($result);
$salt = $row['salt'];
$saltedPW = $escapedPW . $salt;
$hashedPW = hash('sha256', $saltedPW);
$query = "select * from user where name = '$escapedName' and password = '$hashedPW'; ";
# if nonzero query return then successful login
you have to reason in terms of hased password:
store the password as md5('bob123'); when bob is register to your app
$query = "INSERT INTO users (username,password) VALUES('bob','".md5('bob123')."');
then, when bob is logging-in:
$query = "SELECT * FROM users WHERE username = 'bob' AND password = '".md5('bob123')."';
obvioulsy use variables for username and password, these queries are generated by php and then you can execute them on mysql
Please don't use MD5 for password hashing. Such passwords can be cracked in milliseconds. You're sure to be pwned by cybercriminals.
PHP offers a high-quality and future proof password hashing subsystem based on a reliable random salt and multiple rounds of Rijndael / AES encryption.
When a user first provides a password you can hash it like this:
$pass = 'whatever the user typed in';
$hashed_password = password_hash( "secret pass phrase", PASSWORD_DEFAULT );
Then, store $hashed_password in a varchar(255) column in MySQL. Later, when the user wants to log in, you can retrieve the hashed password from MySQL and compare it to the password the user offered to log in.
$pass = 'whatever the user typed in';
$hashed_password = 'what you retrieved from MySQL for this user';
if ( password_verify ( $pass , $hashed_password )) {
/* future proof the password */
if ( password_needs_rehash($hashed_password , PASSWORD_DEFAULT)) {
/* recreate the hash */
$rehashed_password = password_hash($pass, PASSWORD_DEFAULT );
/* store the rehashed password in MySQL */
}
/* password verified, let the user in */
}
else {
/* password not verified, tell the intruder to get lost */
}
How does this future-proofing work? Future releases of PHP will adapt to match faster and easier to crack encryption. If it's necessary to rehash passwords to make them harder to crack, the future implementation of the password_needs_rehash() function will detect that.
Don't reinvent the flat tire. Use professionally designed and vetted open source code for security.
Insertion:
INSERT INTO ... VALUES ('bob', MD5('bobspassword'));
retrieval:
SELECT ... FROM ... WHERE ... AND password=md5('hopefullybobspassword');
is how'd you'd do it directly in the queries. However, if your MySQL has query logging enabled, then the passwords' plaintext will get written out to this log. So... you'd want to do the MD5 conversion in your script, and then insert that resulting hash into the query.
PHP has a method called md5 ;-) Just $password = md5($passToEncrypt);
If you are searching in a SQL u can use a MySQL Method MD5() too....
SELECT * FROM user WHERE Password='. md5($password) .'
or
SELECT * FROM ser WHERE Password=MD5('. $password .')
To insert it u can do it the same way.
Why don't you use the MySQL built in password hasher:
http://dev.mysql.com/doc/refman/5.1/en/password-hashing.html
mysql> SELECT PASSWORD('mypass');
+-------------------------------------------+
| PASSWORD('mypass') |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+
for comparison you could something like this:
select id from PassworTable where Userid='<userid>' and Password=PASSWORD('<password>')
and if it returns a value then the user is correct.
I'm not amazing at PHP, but I think this is what you do:
$password = md5($password)
and $password would be the $_POST['password'] or whatever
just get the hash by following line and store it into the database:
$encryptedValue = md5("YOUR STRING");
To increase security even more, You can have md5 encryption along with two different salt strings, one static salt defined in php file and then one more randomly generated unique salt for each password record.
Here is how you can generate salt, md5 string and store:
$unique_salt_string = hash('md5', microtime());
$password = hash('md5', $_POST['password'].'static_salt'.$unique_salt_string);
$query = "INSERT INTO users (username,password,salt) VALUES('bob','".$password."', '".$unique_salt_string."');
Now you have a static salt, which is valid for all your passwords, that is stored in the .php file. Then, at registration execution, you generate a unique hash for that specific password.
This all ends up with: two passwords that are spelled exactly the same, will have two different hashes. The unique hash is stored in the database along with the current id. If someone grab the database, they will have every single unique salt for every specific password. But what they don't have is your static salt, which make things a lot harder for every "hacker" out there.
This is how you check the validity of your password on login.php for example:
$user = //username input;
$db_query = mysql_query("SELECT salt FROM users WHERE username='$user'");
while($salt = mysql_fetch_array($db_query)) {
$password = hash('md5',$_POST['userpassword'].'static_salt'.$salt[salt]);
}
This method is very powerful and secure. If you want to use sha512 encryption, just to put that inside the hash function instead of md5 in above code.
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'";