-->

password_hash, password_verify, MySQL misunderstan

2020-01-29 16:52发布

问题:

I've updated the code and the script is still returning the "Fail." message. I must be missing something, I've taken everyone's advice. That or I'm just plain stupid LOL! Here's the updated code:

require('../connect.php');
$username = $_POST['username-sign-in'];
$password = $_POST['password-sign-in'];
if true then exit() for {
    empty($username);
    empty($password);
}
if (isset($username, $password)) {
    $getuser = $connection->prepare('SELECT `username`, `password`
                       FROM `users` WHERE `username` = ?');
    $getuser->bind_param('s', $username);
    $getuser->execute();
    $userdata = $getuser->get_result();
    $row = $userdata->fetch_array(MYSQLI_ASSOC);
    echo 'Password from form: ' . $password . '<br />';
    echo 'Password from DB: ' . $row['password'] . '<br />';
    if (password_verify($password, $row['password'])) {
        echo 'Success.';
        exit();
    }
    else {
        echo 'Fail.';
        exit();
    }
}
else {
    echo 'Please enter your username and password.';
    $connection->close();
    exit();
}

signup.php

require('../connect.php');
$ip = $_SERVER['REMOTE_ADDR'];
$username = $_POST['username-sign-up'];
$password = $_POST['password-sign-up'];
$hashedpassword = password_hash($_POST['password-sign-up'], 
                       PASSWORD_BCRYPT, ['cost' => 12]);
$email = strtolower($_POST['email-sign-up']);
if true then exit() for {
    empty($username)
    empty($password)
    empty($email)
    !filter_var($email, FILTER_VALIDATE_EMAIL)
    strlen($username) < 2 || strlen($username) > 32
    strlen($password) < 6 || strlen($password) > 32
}
$usernameandemailcheck = $connection->prepare('SELECT `username`, `email` 
                       FROM `users` WHERE `username` = ? AND `email` = ?');
$usernameandemailcheck->bind_param('ss', $username, $email);
$usernameandemailcheck->execute();
$result = $usernameandemailcheck->get_result();
$row = $result->fetch_array(MYSQLI_ASSOC);
// .. Username and email validation
if (isset($username, $hashedpassword, $email)) {
    // Create and send mail
    $query = $connection->prepare('INSERT INTO users (`ip`, `username`, 
                       `password`, `email`) VALUES (?, ?, ?, ?)');
    $query->bind_param('ssss', $ip, $username, $hashedpassword, $email);
    $query->execute();
    // SUCCESS
}
else {
    // FAILURE
}

回答1:

You can't hash the input and then query against that in the database, as the hash will use a different random salt each time. So you could hash the same password a thousand times and get 1000 different results.

You need to simply just query the DB for the record related to the username, then compare the password hash returned from the DB with the input password using password_verify().

Also, when initially writing the hash to the DB on password creation (using password_hash()) there is no need to escape the hash. password_hash() is not used at all in the password verification process.



回答2:

From a quick glance at the functions it seems that you may have got the test the wrong way round.

You should store the hashed version of the password in the db and then compare that with the password provided through the $_POST.. then the rest would go like..

$getuser = $connection->prepare('SELECT `password` 
                                        FROM `users` WHERE `username` = ?');
$getuser->bind_param('s', $username);
$getuser->execute();
$userdata = $getuser->get_result();
$row = $userdata->fetch_array(MYSQLI_ASSOC);
echo 'Password from form: ' . $hashedpassword . '<br />';
echo 'Password from DB: ' . $row['password'] . '<br />';
if (password_verify($password, $row['password'])) {
    // $password being $_POST['password-sign-in']
    // $row['password'] being the hashed password saved in the database
    echo 'Success.';
    exit();
} else {
    echo 'Fail.';
    exit();
}


回答3:

Have you attempted to change the single quotes like ':

$getuser = $connection->prepare('SELECT `username`, `password` FROM `users` WHERE `username` = ? AND `password` = ?');
$getuser->bind_param('ss', $username, $hashedpassword);

To double quotes like "

$getuser = $connection->prepare("SELECT `username`, `password` FROM `users` WHERE `username` = ? AND `password` = ?");
$getuser->bind_param("ss", $username, $hashedpassword);

Also, why are you matching against password? Perhaps this would work for your test case:

$getuser = $connection->prepare("SELECT `username`, `password` FROM `users` WHERE `username` = ?");
$getuser->bind_param("s", $username);

EDIT Also, you are essentially double-hashing the password when you do your check:

if (password_verify($row['password'], $hashedpassword)) {

Just do this instead:

if (password_verify($password, $row['password'])) {

The issue is that password_verify has the syntax of:

boolean password_verify ( string $password , string $hash )

You need to send the plain/non-hashed password in the first param & then place the hashed value in the second param. Try it out.



回答4:

the problem is usually when you hash user input from a form it is hashed as if it has double quotes. that's why you see the hash starting with $2y$10...... I think you should try to solve it from this point. if you are giving an answer also consider this