我正在使用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
找到了问题。 当我这样做时:
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 个字符?
我在使用 password_verify(( 时遇到了同样的问题。对我来说,我已经将我的用户名和密码声明为 VARCHAR(50(。因此,它没有在我的数据库中插入显然超过 50 个字符的哈希值。因此,每次我使用 password_verify(( 时,我都会得到一个假的。我将数据库值更改为 varchar(255(。再次插入数据,经过测试,它可以工作。
仅供将来参考。我遇到了密码无缘无故失败的相同问题。当我仔细查看它时,我发现数据库中的密码字段不够大,无法存储完整的哈希值,因此一些字符被切断了。增加数据库字段的大小后,它运行良好。
我遇到了同样的问题,它不起作用,由于某种原因,它似乎有助于放置:
$hash = substr( $hash, 0, 60 );
进入代码,尽管我的字符串已经有 60 个字符长。
我遇到了同样的问题,尽管确保我的数据库列是 varchar(255(,哈希为 60 个字符,并确保我的编码始终是 UTF-8,但它仍然不起作用。我对 PHP 和 SQL 很陌生,所以我不会假装确切地理解它为什么有效,但我设法修复了它,所以我希望这篇文章能帮助其他人解决同样的问题。
事实证明,password_verify(( 没有验证我的哈希的根本原因是因为我在关闭并重新打开连接以执行下一个查询之前,在脚本中创建了一个预准备语句,该语句使用了脚本前面的存储过程,而没有正确获取查询中的所有结果以清除缓冲区。关闭语句后在mysqli_link上调用 next_result(( 将确保使用任何结果。
此外,我随后使用另一个带有存储过程的预准备语句来插入密码,但我仍然需要调用 store_result(( 和 free_result((,即使插入中没有返回任何结果集。我假设这些东西的组合正在沿线某处损坏我的数据,导致 password_verify(( 在看似相同的哈希上返回 false。
这个答案是针对一个不同的问题,但我发现它对于学习如何使用存储过程正确关闭预准备语句很有用。