我使用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
答案 0 :(得分:12)
发现问题。当我这样做时:
echo strlen($hash)
它打印90,这很奇怪,因为当我打印出成功/失败消息时,最后肯定没有空格,并且该字段的varchar长度为255
我添加了这一行:
$hash = substr( $hash, 0, 60 );
现在它运作正常。
奇怪的是,没有其他人似乎遇到过这个问题。有关于password_verify的类似帖子,但没有一个需要此类转化或任何转换:
php password_verify not working
http://forums.phpfreaks.com/topic/283407-need-help-with-password-verify/
Using PHP 5.5's password_hash and password_verify function
困扰我的一件事是这会阻止代码向前兼容。当默认更改时,我怎么知道哈希值是60个字符?
答案 1 :(得分:3)
password_verify()遇到相同的问题。对我来说,我已将用户名和密码声明为VARCHAR(50)。因此,它没有在我的数据库中插入明显超过50个字符的哈希值。因此,每次我使用password_verify()时,我都会得到一个错误。我将数据库值更改为varchar(255)。再次插入数据,经过测试,即可正常工作。
答案 2 :(得分:2)
仅供将来参考。我有同样的问题密码失败无缘无故。当我仔细观察它时,我发现数据库中的密码字段不足以存储完整的哈希值,因此某些字符被切断了。增加数据库字段的大小后,它完美地工作。
答案 3 :(得分:2)
我有同样的问题,因为它似乎有助于推出:
$hash = substr( $hash, 0, 60 );
尽管我的字符串长度已经是60个字符。
答案 4 :(得分:1)
我遇到了同样的问题,尽管确保我的数据库列是varchar(255),哈希值是60个字符,并且确保我的编码一直是UTF-8,但仍然无法正常工作。我对PHP和SQL很陌生,所以我不会假装明白为什么它有效,但我设法修复它,所以我希望这篇文章能帮助其他人解决同样的问题。
事实证明,password_verify()没有验证我的哈希值的根本原因是因为我做了一个准备好的语句,它在脚本中使用了一个存储过程而没有正确地从查询中获取所有结果来清除缓冲区,在关闭和重新打开连接之前执行下一个查询。关闭语句后在mysqli_link上调用next_result()将确保消耗任何结果 另外,我当时正在使用另一个带有存储过程的预准备语句来为密码插入,但是我仍然需要调用store_result()和free_result(),即使插入没有返回结果集。我假设这些东西的组合在整个行的某处破坏了我的数据,导致password_verify()在看似相同的哈希值上返回false。
This answer是针对不同的问题,但我发现它对于学习如何使用存储过程正确关闭预准备语句很有用。