php password_verify not working with database

2019-01-09 04:11发布

I am using php 5.4 with this backwards compatibility script: https://github.com/ircmaxell/password_compat/blob/master/lib/password.php

that shouldn't matter though, because I can get the hashing and verification process working in my registration function:

$hash = password_hash($pass, PASSWORD_DEFAULT);

echo $pass;
echo $hash;

if( password_verify($pass,$hash) )
    echo 'success';
else echo 'failure';

//success is always shown

//EXAMPLE INPUT
$pass = 'password';

//EXAMPLE OUTPUT
password$2y$10$JK1jumvvSIm/gP3fWE3k9O98MzvHKDRYCjRPBniYg9riACyQw7WYSsuccess

but whenever I try to store the hash in a MySQL database and then retrieve it for the verify function, it always fails. Here is my login function:

function user_login( $mysqli, $email, $pass ){    

        $err_msg = 'login: '.$mysqli->error.' | '.$email;

        if( $stmt = $mysqli->prepare('SELECT password FROM users WHERE email=?') ) :

            if( !$stmt->bind_param('s', $email) ) log_sql_error( $err_msg );
            if( !$stmt->execute() ) log_sql_error( $err_msg );
            if( !$stmt->bind_result( $hash ) ) log_sql_error( $err_msg );
            if( $stmt->fetch() === FALSE ) log_sql_error( $err_msg );
            if( !$stmt->close() ) log_sql_error( $err_msg );

            //I can see that these values are identical to the ones
            //echoed out in the registration function
            echo $pass;
            echo $hash;

            if( password_verify($pass,$hash) )
                echo 'success';
            else echo 'failure';

        else : log_sql_error( $err_msg );
        endif;
}
//failure is always shown

//EXAMPLE INPUT
$pass = 'password';

//EXAMPLE OUTPUT
password$2y$10$JK1jumvvSIm/gP3fWE3k9O98MzvHKDRYCjRPBniYg9riACyQw7WYSfailure

My 'password' column has this datatype: VARCHAR(255) NOT NULL

No php errors show up so the only thing I can think of is that the hash value is not formatted in the same way when it comes out of the database as when it went in, but when I echo out the values, they appear to be identical.

How else can I debug this / what is wrong with my code?

Thanks

UPDATE:

This definitely has something to do with encoding:

$hardcode_hash = '$2y$10$JK1jumvvSIm/gP3fWE3k9O98MzvHKDRYCjRPBniYg9riACyQw7WYS';

echo $hash;
echo '<br/>';
echo $hardcode_hash;
echo '<br/>';

if( $hash == $hardcode_hash )
    echo 'success';
else echo 'failure';

//OUTPUT
$2y$10$JK1jumvvSIm/gP3fWE3k9O98MzvHKDRYCjRPBniYg9riACyQw7WYS
$2y$10$JK1jumvvSIm/gP3fWE3k9O98MzvHKDRYCjRPBniYg9riACyQw7WYS
failure

how do I reformat the SQL value to match the output of password_hash? Here's what I've tried:

(string)$hash
utf8_encode($hash)

if I do:

$hash = settype($hash,"string");

if($hash == $hardcode_hash) returns true, but password_verify($pass, $hash) still returns false

4条回答
Summer. ? 凉城
2楼-- · 2019-01-09 04:30

I had the same issue and it was still not working despite ensuring my database columns were varchar(255), that the hashes were 60 characters, and ensuring my encoding was UTF-8 all the way through. I'm pretty new to PHP and SQL so I won't pretend to understand exactly why it worked, but I managed to fix it so I hope this post will help other folks with the same problem.

It turned out that the underlying reason password_verify() wasn't verifying my hashes was because I had made a prepared statement that used a stored procedure earlier in the script without fetching all the results from the query properly to clear the buffer, before closing and reopening the connection to perform the next query. Calling next_result() on the mysqli_link after closing the statement will make sure any results are consumed.
Additionally, I was then using another prepared statement with a stored procedure to make the insert for the password, but I still needed to make calls to store_result() and free_result() even though no result sets were returned from the insert. I'm assuming the combination of these things was corrupting my data somewhere along the line, resulting in password_verify() returning false on seemingly identical hashes.

This answer was for a different problem but I found it useful for learning how to properly close out prepared statements with stored procedures.

查看更多
闹够了就滚
3楼-- · 2019-01-09 04:32

Found the problem. when I did this:

echo strlen($hash)

it printed 90, which is strange because there were definitely no spaces at the end when I printed out the success/failure message, and the field has a varchar length of 255

I added this line:

$hash = substr( $hash, 0, 60 );

And now it works fine.

Its strange that no one else seems to have run into this issue. There are similar posts about password_verify, but none of them required this type of conversion, or any conversion for that matter:

php password_verify not working

password_verify php not match

http://forums.phpfreaks.com/topic/283407-need-help-with-password-verify/

Using PHP 5.5's password_hash and password_verify function

One thing that bothers me is this prevents the code from being forward compatible. How will I know that the hash is 60 characters long when the default changes?

查看更多
混吃等死
4楼-- · 2019-01-09 04:37

Just for future reference. I had the same issue with passwords failing for no reason. When I took a closer look at it I saw that the password field in the database was not big enough to store the full hash so some characters were cut off. After increasing the size of the database field it worked perfectly.

查看更多
别忘想泡老子
5楼-- · 2019-01-09 04:41

I had the same issue you had with it not working, for some reason it seems to help putting the:

$hash = substr( $hash, 0, 60 );

into the code although my string was already 60 characters long.

查看更多
登录 后发表回答