问题 php password_verify不使用数据库


我使用PHP 5.4与这个向后兼容脚本: https://github.com/ircmaxell/password_compat/blob/master/lib/password.php

但这应该不重要,因为我可以在我的注册函数中使用散列和验证过程:

$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

但每当我尝试将哈希值存储在MySQL数据库中,然后为验证函数检索它时,它总是会失败。这是我的登录功能:

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

我的“密码”列有以下数据类型: VARCHAR(255) NOT NULL

没有php错误出现所以我唯一可以想到的是,当它从数据库出来时,哈希值的格式不同,就像它进入时一样,但当我回显出值时,它们似乎是相同。

我怎么能调试这个/我的代码有什么问题?

谢谢

更新:

这肯定与编码有关:

$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

如何重新格式化SQL值以匹配password_hash的输出?这是我尝试过的:

(string)$hash
utf8_encode($hash)

如果我做:

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

if($hash == $hardcode_hash) 返回true,但是 password_verify($pass, $hash) 仍然返回false


6260
2017-12-22 21:05


起源

谁没有留下评论或答案就投票问题?没有帮助...... - Cbas
大多数SO这些天:-) - user4166144
你可以展示样本 $pass 和 $hash 输出。没人能够测试您的代码摘录。 - mario
使用 var_dump。如果字符串真的相同,则在第二种情况下不可能失败。 - mario
您是否检查了您的页面/ db的编码,是否有可能一个变量包含多字节字符串,而另一个变量不包含多字节字符串? - martinstoeckli


答案:


发现了问题。当我这样做时:

echo strlen($hash)

它打印90,这很奇怪,因为当我打印出成功/失败消息时,最后肯定没有空格,并且该字段的varchar长度为255

我添加了这一行:

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

现在它工作正常。

奇怪的是,没有其他人似乎遇到过这个问题。有关于password_verify的类似帖子,但它们都不需要此类转换或任何转换:

php password_verify无法正常工作

password_verify php不匹配

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

使用PHP 5.5的password_hash和password_verify函数

困扰我的一件事是这会阻止代码向前兼容。当默认更改时,我怎么知道哈希值是60个字符?


11
2017-12-23 01:31



这应该是没有必要的。你确定它是一个varchar字段而不是char *吗?也许你也可以看看这个小 文章 关于使用UTF-8进行Php(编码)和数据库(charset)。将哈希插入数据库的代码也很有趣。 - martinstoeckli
添加到@martinstoeckli的输入,最好使用 CHAR 数据库中哈希密码字段的数据类型,并将60设置为使用时的长度 PASSWORD_BCRYPT 不变。在这种情况下,哈希值总是60个字符。 - Ashesh Kumar Singh
您与其他解决方案的链接让我得到答案。谢谢!特别是“password_verify not working”导致我的解决方案。 - Timothy Steele
@Cbas我有同样的问题。我尝试修剪($ hash)而不是substr(),它的工作原理! - otrebla


仅供将来参考。我有同样的问题,密码无故失败。当我仔细观察它时,我发现数据库中的密码字段不足以存储完整的哈希值,因此某些字符被切断了。增加数据库字段的大小后,它完美地工作。


2
2018-03-31 22:24





我有同样的问题你没有工作,由于某种原因它似乎有助于把:

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

虽然我的字符串已经有60个字符长,但仍然在代码中。


2
2018-05-05 16:00





我遇到了同样的问题,尽管确保我的数据库列是varchar(255),哈希值是60个字符,并确保我的编码一直是UTF-8,但仍然无法正常工作。我是PHP和SQL的新手,所以我不会假装理解为什么它有效,但我设法修复它,所以我希望这篇文章能帮助其他人解决同样的问题。

事实证明,password_verify()没有验证我的哈希的根本原因是因为我已经制作了一个准备好的语句,该语句在脚本中使用了一个存储过程而没有正确地从查询中获取所有结果来清除缓冲区,重新打开连接以执行下一个查询。关闭语句后在mysqli_link上调用next_result()将确保消耗任何结果。
另外,我当时正在使用另一个带有存储过程的预准备语句来为密码插入,但我仍然需要调用store_result()和free_result(),即使插入没有返回结果集。我假设这些东西的组合在整个行的某个地方破坏了我的数据,导致password_verify()在看似相同的哈希值上返回false。

这个答案 是一个不同的问题,但我发现它有助于学习如何使用存储过程正确关闭准备好的语句。


0
2018-04-01 13:10