password_hash, password_verify, MySQL misunderstanding?

slicksct picture slicksct · Jan 12, 2014 · Viewed 13.1k times · Source

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
}

Answer

Mike Brant picture Mike Brant · Jan 12, 2014

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.