php password_verify not working with database

49,771

Solution 1

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?

Solution 2

I was having the same issue with password_verify(). For me i had declared my username and password as VARCHAR(50). Therefore it was not inserting the hash value in my database which is obviously more than 50 characters. Therefore every time I used password_verify() I got a false. I changed my database values to varchar(255). Inserted data again, tested and it works.

Solution 3

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.

Solution 4

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.

Solution 5

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.

Share:
49,771

Related videos on Youtube

Cbas
Author by

Cbas

Updated on July 09, 2022

Comments

  • Cbas
    Cbas almost 2 years

    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

    • mario
      mario over 9 years
      You could showcase the sample $pass and $hash output. Noone is able to testrun your code excerpt.
    • Cbas
      Cbas over 9 years
      added example outputs - as you can see, the strings are identical, but the second password_verify fails
    • mario
      mario over 9 years
      Use var_dump. If the strings were really identical, it couldn't possibly fail in the second case.
    • martinstoeckli
      martinstoeckli over 9 years
      Did you check the encoding of your pages/db, is it possible that one variable contains a multibyte string, while the other doesn't?
    • Cbas
      Cbas over 9 years
      I think you're right about the encoding being different, but I don't know what pages/db is or how to figure out which encoding my database is in - I've been trying various string conversions with no success so far
  • martinstoeckli
    martinstoeckli over 9 years
    This should really not be necessary. Are you sure it is a varchar field and not char*? Maybe you could also have a look at this small article about using UTF-8 for Php (encoding) and database (charset). The code which is inserting the hash into the database could be interesting too.
  • Ashesh
    Ashesh about 9 years
    adding to inputs of @martinstoeckli, it would be better to use CHAR data type for the hashed password field in the database and set 60 as its length on using PASSWORD_BCRYPT constant. In this case the hash will always be 60 characters long.
  • Timothy Steele
    Timothy Steele about 8 years
    Your links to other solutions led me to the answer. Thanks! Specifically it was "password_verify not working" that lead me to my solution.
  • Don Beto
    Don Beto about 8 years
    @Cbas I had the same issue. I tried trim($hash) instead of substr() and it works!
  • HADI
    HADI over 2 years
    I had the same issue. Glad you commented! Thank you.