I have a processing file for a login to an app. I either do not understand the purpose of password_needs_rehash() or it is not working. The login is authenticating and passing me through to the correct page. But I can't get the code to even echo the new hash.
Am I doing this correctly?
Does the if not throw a new hash because it does not need rehashed? If so when would a password need rehashed if it was properly hashed and stored in the DB?
My processing file is below:
$hash = $row['hash'];
$userPassword = $_POST["li_password"];
if (password_verify($userPassword, $hash)) {
if ( password_needs_rehash($hash, PASSWORD_DEFAULT, ['cost' => 12]) ) {
$newhash = password_hash($userPassword, PASSWORD_DEFAULT, ['cost' => 12]);
echo $newhash;
}
} else {
header('Location: http://' . $_SERVER['HTTP_HOST'] . '?error=loginfailed');
exit();
}
The function password_needs_rehash() only needs to be used if you change the $options which usually refers to the cost.
The more the cost, the more CPU time it takes to hash the password but the more difficult it becomes to crack it. If you change hosting or move over to a cloud based system where multiple computers can calculate the hash for you, you are able to increment it at your own discretion.
You only need to check if the password needs a rehash at user login, since password_verify() can still verify the password if you changed the $options. If password_needs_rehash() returns true at that point, use password_hash() with the new options and replace the old hash.
if (password_verify($_POST["li_password"], $row['hash'])) {
// valid login
if (password_needs_rehash($row['hash'], PASSWORD_DEFAULT, $options = ['cost' => 12])) {
$newhash = password_hash($_POST["li_password"], PASSWORD_DEFAULT, $options);
// store new hash in db.
}
} else {
header('Location: http://' . $_SERVER['HTTP_HOST'] . '?error=loginfailed');
exit();
}
Related
I'm using PHP's password hashing API to hash and verify my passwords on a site I'm building, however whenever I try and verify my password it always returns false.
I have a User class which sets the password before they are inserted into the database:
public function set__password($passwd) {
self::$password = password_hash($passwd, PASSWORD_BCRYPT, array('cost' => 12));
}
If the username and email is unique the new user row is inserted - upon checking my database I have what seems to be a valid BCRYPT string for my password:
$2y$12$lTMEP0wevDEMX0bzStzoyOEzOTIAi3Hyhd3nYjGwzbI
To verify my password, I run the following script:
$username = $_POST['username'];
$password = $_POST['password'];
$DB = Database::getInstance();
// Get the stored password hash
$res = $DB->run__query('SELECT password FROM users WHERE username = "' . $username . '"');
$hash = $res[0]['password'];
// Do the passwords match?
if(password_verify($password, $hash)) {
echo 'success';
} else {
echo 'failed';
}
$hash pertains to the string quoted above, however when I then call password_verify($password, $hash) where $password is the plain-text password retrieved from my input field, I always receive a value of false.
The given hash string example has 50 characters instead of 60. Double-Check the database - CHAR(60) - and var_dump($hash).
Other problem that you can have, is when you reduce the cost in the server for gaining time.
Always use password_hash($pass, PASSWORD_DEFAULT), is the best way.
I am using UserCake and ran into an issue. For some reason the generateHash() function is no longer working consistently. Here's what I'm looking at:
funcs.php <-- Where the function is held
function generateHash($plainText, $salt = null) {
if ($salt === null) {
$salt = substr(md5(uniqid(rand(), true)), 0, 25);
} else {
$salt = substr($salt, 0, 25);
}
return $salt . sha1($salt . $plainText);
}
class.newuser.php <-- where the function is called to create the password
//Construct a secure hash for the plain text password
$secure_pass = generateHash($this->clean_password);
login.php <-- where the function is called to compare the passwords
//Hash the password and use the salt from the database to compare the password.
$entered_pass = generateHash($password,$userdetails["password"]);
if($entered_pass != $userdetails["password"]) {
$errors[] = lang("ACCOUNT_USER_OR_PASS_INVALID");
} else {
//Passwords match! we're good to go'
}
I can successfully create a new account. But when I go to log in the hash password created by login.php is different than the one created by the new user class. For example, when I log in I put print_r on both the entered hash pw, and the hash pw in the database and here's what comes back:
$entered_pass = 62b8ce100193434601929323a13a4d95bd3c6535b014e6444516af13f605f36f7
database pass = 62b8ce100193434601929323a153564aaeb4ad75d57b353ee8918cd9829cb5e1b
The only thing I can think of is that the hashed password starts to deviate on the 26th character, and the $salt looks to have something with 25 going on (assuming thats the max length?). All of this is stock UserCake stuff so I don't understand why it is being so inconsistant.
I will note, if I copy the hashed $entered_pass (first one there) and paste it into the database, I will successfully log in.
EDIT >>>
After looking at it some more, I think the problem comes down to sha1($salt . $plainText);. It looks as though after the first $salt is where things begin to differ. Also When I remove the sha1() function it logs in perfectly, I just wonder if that has any major impact on security.
I had this same issue. After some research I found that using the password_hash() function was more up to date.
I changed the $secure_pass var in class.newuser.php to this...
//Construct a secure hash for the plain text password
$secure_pass = password_hash("$this->clean_password", PASSWORD_DEFAULT);
class.user.php
//Update a users password
public function updatePassword($pass)
{
global $mysqli,$db_table_prefix;
$secure_pass = password_hash("$pass", PASSWORD_DEFAULT);
$this->hash_pw = $secure_pass;
$stmt = $mysqli->prepare("UPDATE ".$db_table_prefix."users
SET
password = ?
WHERE
id = ?");
$stmt->bind_param("si", $secure_pass, $this->user_id);
$stmt->execute();
$stmt->close();
}
login.php
// Use built in PHP password hashing
if (!password_verify($password, $userdetails["password"])) {
// Login Error Attempt Handler
login_attm_hand();
//Again, we know the password is at fault here, but lets not give away the combination incase of someone bruteforcing
$errors[] = lang("ACCOUNT_USER_OR_PASS_INVALID");
}
I think that is everything I had to update on my site. If you have any errors let me know and I can try and help.
I'm using PHP's password hashing API to hash and verify my passwords on a site I'm building, however whenever I try and verify my password it always returns false.
I have a User class which sets the password before they are inserted into the database:
public function set__password($passwd) {
self::$password = password_hash($passwd, PASSWORD_BCRYPT, array('cost' => 12));
}
If the username and email is unique the new user row is inserted - upon checking my database I have what seems to be a valid BCRYPT string for my password:
$2y$12$lTMEP0wevDEMX0bzStzoyOEzOTIAi3Hyhd3nYjGwzbI
To verify my password, I run the following script:
$username = $_POST['username'];
$password = $_POST['password'];
$DB = Database::getInstance();
// Get the stored password hash
$res = $DB->run__query('SELECT password FROM users WHERE username = "' . $username . '"');
$hash = $res[0]['password'];
// Do the passwords match?
if(password_verify($password, $hash)) {
echo 'success';
} else {
echo 'failed';
}
$hash pertains to the string quoted above, however when I then call password_verify($password, $hash) where $password is the plain-text password retrieved from my input field, I always receive a value of false.
The given hash string example has 50 characters instead of 60. Double-Check the database - CHAR(60) - and var_dump($hash).
Other problem that you can have, is when you reduce the cost in the server for gaining time.
Always use password_hash($pass, PASSWORD_DEFAULT), is the best way.
I'm using a script that ircmaxell wrote called password_compat. I thought I followed his instructions correctly, but I cannot seem to get my password verified using password_verify($password, $hash).
The hashed password saved in my database is;
$2y$10$zYpSzIj7kTPv3H7wDI/uXSYqi1se46b38uumP6SM4XGMmsjU3q
I'm using PDO to grab my hashed password and using password_verify($password, $hash) to compare what the login form is posting. It's my understanding that BRCYPT is not a hashing function so password_verify($password, $hash) will do it's magic. I have no idea how the salt is created, but I would think it creates a custom salt for every new password, but how it can compare it to my saved password baffles me. How does it match the correct salt with the password? This whole not saving the salt in my database kind of confuses me, lol. Here is the code I'm using;
bcrypt
if($login->verifyip($_SERVER['REMOTE_ADDR']))
{
require_once 'password.php'; //password_compat supplied file
$username = $_POST['username'];
$password = $_POST['password'];
$dbpassword = $login->GetPassword($username); // pull saved password from db
// verify posted password with saved password
if(password_verify($dbpassword, $password))
{
echo 'verified';
}
else
{
echo 'not verified';
}
}
PDO
public function GetPassword($username)
{
$passwordSQL = 'CALL get_password(:_user)'; // using stored procedure
try
{
$pdo = new PDO('my login stuff');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$password = $pdo->prepare($passwordSQL);
$password->bindParam(':_user',$username);
$password->execute();
$fetch = $password->fetchColumn(0);
$password->closeCursor();
return $fetch;
}
catch(PDOException $e)
{
return 'error' . $e->getMessage();
exit();
}
}
I removed $hash like blender suggested.
Thanks for having a look :)
password_verify's arguments are the other way around:
password_verify($password, $dbpassword)
As for how it works, the hash is of this form:
$<algorithm>$<cost>$<salt>/<hash>
So from the hash:
$2y$10$zYpSzIj7kTPv3H7wDI/uXSYqi1se46b38uumP6SM4XGMmsjU3q
You can see that the cost is 10, the salt is zYpSzIj7kTPv3H7wDI and that bcrypt(salt + password) is uXSYqi1se46b38uumP6SM4XGMmsjU3q.
password_verify extracts that information from your supplied hash and just checks if bcrypt(salt + password) == hash.
I have registered a new user and saved the username, password & salt in the DB using the following hashing method:
if(isset($_POST['register']))
{
$password = $_POST['password']
function sanitize($data)
{
$data=trim($data);
$data=htmlspecialchars($data);
$data=mysql_real_escape_string($data);
return $data;
}
$password = sanitize($password);
function createSalt()
{
$salt = bin2hex(mcrypt_create_iv(32,MYCRYPT_DEV_URANDOM));
$hash = hash("sha256", $salt);
$final = $salt.$hash;
return $final;
}
$hashedPassword = hash("sha256", $password);
$salt = createSalt();
$hashedPassword = hash("sha256", $hashedPassword.$salt);
$query = sprintf("INSERT INTO users(username, password, salt) VALUES('%s','%s','%s')",$username, $hashedPassword, $salt);
}
And Later while trying the login.php, I am entering the same password which I saved during registration and using the below code to check if the entered password is the same as the one in the DB
if(isset($_POST['login']]))
{
$password = $_POST['password']
function sanitize($data)
{
$data=trim($data);
$data=htmlspecialchars($data);
$data=mysql_real_escape_string($data);
return $data;
}
function validateUser()
{
session_regenerate_id (); //this is a security measure
$_SESSION['valid'] = 1;
$_SESSION['username'] = $username;
}
$password = sanitize($password);
$query = sprintf("SELECT * FROM users WHERE username = '%s'",$username);
$sql = mysql_query($query);
$count = mysql_num_rows($sql);
$row = mysql_fetch_array($sql);
if($count<1)
{
echo $count;
unset($_POST['login']);
header("location:login.php");
exit;
}
$hash = hash("sha256", $password);
$salt = $row['salt'];
$hash = hash("sha256",$hash.$salt);
echo $hash."<br />".$row['password']."<br /><br />";
if($hash != $row['password'])
{
unset($_POST['login']);
header("location:login.php");
exit;
}
else
{
validateUser();
unset($_POST['login']);
header("location:index.php");
exit;
}
}
These passwords are not getting matched.
Kindly let me know what's wrong in this code.
There is nothing wrong with your code.
the salt value stored in the database is truncated because the varchar value is low increase the varchar value of your salt column to 200-300 something and than try this.. it will run fine.
I facepalmed when I found out this was screwing the result..
Dins
Actually i didn't see why this should not work, the code you have shown, should produce the same value, maybe you could check, whether the salt you read from the database is really the same as you wrote to the database.
Nevertheless i would not engourage to go further on this route, there are quite a lot of problems here.
First of all, SHA-256 is not a good choice to hash passwords, instead use a slow key-derivation function like BCrypt.
You should not escape input data without need, and if you need to escape them, you should do it only for the specific target system (htmlspecialchars and mysql_real_escape_string make no sense if you are going to calculate a hash anyway).
To create a salt, you use the random source, that is good. Using a hash afterwards creating the salt, will in no way make the salt more random.
There is no need to have two separate fields for password and salt in the database. Php's crypt() function will create a hash value, that already contains the salt.
I would invite you to read this tutorial about hashing passwords, you will find a PHP example too, and i would recommend to use the phpass library.