20

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

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
Cbas
  • 6,003
  • 11
  • 56
  • 87
  • 1
    You could showcase the sample `$pass` and `$hash` output. Noone is able to testrun your code excerpt. – mario Dec 22 '14 at 21:19
  • added example outputs - as you can see, the strings are identical, but the second password_verify fails – Cbas Dec 22 '14 at 21:42
  • 1
    Use `var_dump`. If the strings were really identical, it couldn't possibly fail in the second case. – mario Dec 22 '14 at 22:20
  • 2
    Did you check the encoding of your pages/db, is it possible that one variable contains a multibyte string, while the other doesn't? – martinstoeckli Dec 22 '14 at 22:37
  • 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 – Cbas Dec 23 '14 at 01:21

5 Answers5

19

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?

Community
  • 1
  • 1
Cbas
  • 6,003
  • 11
  • 56
  • 87
  • 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](http://www.martinstoeckli.ch/php/php.html#utf8) about using UTF-8 for Php (encoding) and database (charset). The code which is inserting the hash into the database could be interesting too. – martinstoeckli Dec 23 '14 at 10:09
  • 2
    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. – Ashesh Apr 15 '15 at 07:12
  • 2
    Your links to other solutions led me to the answer. Thanks! Specifically it was "password_verify not working" that lead me to my solution. – Timothy Steele Mar 13 '16 at 06:07
  • 1
    @Cbas I had the same issue. I tried trim($hash) instead of substr() and it works! – Don Beto Apr 08 '16 at 04:05
13

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.

MosesK
  • 359
  • 3
  • 7
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.

Kobus
  • 101
  • 1
  • 4
2

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.

Lexix
  • 21
  • 1
1

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.

DBC
  • 11
  • 2