Crypt function doesn't work when comparing string to hash
我使用的是一种非常标准的cookie登录方式——我给用户两个cookie,一个使用用户名,另一个使用随机生成的字符串和用户特定的salt。
登录时会发生以下情况:
1 2 3 4 5 6 7 8 9 10 11 12 | $_SESSION['username']=$row[username]; $_SESSION['user_id']=$row['id']; $loginhash=generateRandomBase64String()."_".$row['salt']; $number_of_days = 14; $date_of_expiry = time() + 60 * 60 * 24 * $number_of_days ; setcookie("userlogin", $row['username'], $date_of_expiry,"/" ) ; setcookie("loginhash", $loginhash, $date_of_expiry,"/" ) ; $cryptedhash=crypt($loginhash); $today=date("Y-m-d"); mysql_query("update members set last_login='$today',loginhash='$cryptedhash' where id='$row[id]'") or die(mysql_error()); |
因此,
我使用会话变量(
1 2 3 4 5 6 7 8 9 10 11 12 | if($_COOKIE['userlogin'] && !isset($_SESSION[user_id])){ $username=mysql_real_escape_string($_COOKIE['userlogin']); $loginhash=mysql_real_escape_string($_COOKIE['loginhash']); $salt=substr($loginhash,-8); $result=mysql_query("select * from members where (username='$username' || email='$username') && salt='$salt' limit 1") or die (mysql_error()); $row=mysql_fetch_assoc($result); $cryptedhash=$row['loginhash']; if (crypt($loginhash, $cryptedhash) == $cryptedhash){ $_SESSION['username']=$row[username]; $_SESSION['user_id']=$row['id']; } } |
号
有人能看到这里出了什么问题吗?
ps我不想在这里开始讨论cookie安全性或各种可用的散列函数。
问题是:
在对crypt()的第一个调用中,不指定salt。在对crypt()的第二个调用中,您将$cryptedHash作为salt传递。
如果不提供随机salt,则记录
不幸的是,使用的算法和salt+哈希的长度/格式是基于操作系统、PHP版本以及是否指定salt参数的组合。当您以前使用代码时,您遇到了一个愉快的意外,即在对crypt()的两个调用中都选择了des。现在,您的环境对crypt()的两个调用使用了不同的算法,因为您只在其中一个调用中提供了散列。
解决方案是向crypt()的两个调用传递一致的salt。您可以停止将salt附加到要散列的字符串中,并实际将用户salt作为salt参数传递,一切都会好起来的。
如果您使用的是5.5.0或更高版本的PHP,那么您应该看看phpDoc-密码哈希!我知道你说过你不想讨论不同的散列函数,但我认为这一个让你更容易,因为它应该摆脱你的问题,而且(在我看来)更容易使用!
下面是您的新函数代码:(未测试,如果中断请评论)
1 2 3 4 5 6 7 8 9 10 11 12 13 | $_SESSION['username']=$row[username]; $_SESSION['user_id']=$row['id']; $loginhash=generateRandomBase64String()."_".$row['salt']; $number_of_days = 14; $date_of_expiry = time() + 60 * 60 * 24 * $number_of_days ; setcookie("userlogin", $row['username'], $date_of_expiry,"/" ) ; setcookie("loginhash", $loginhash, $date_of_expiry,"/" ) ; $cryptedhash=password_hash($loginhash, PASSWORD_DEFAULT, array("cost" => 10)); // a cost of 10 is standard, you may want to adjust it according to your hardware (lower/higher cost means faster/slower) $today=date("Y-m-d"); mysql_query("update members set last_login='$today',loginhash='$cryptedhash' where id='$row[id]'") or die(mysql_error()); |
使用
和
1 2 3 4 5 6 7 8 9 10 11 12 | if($_COOKIE['userlogin'] && !isset($_SESSION[user_id])){ $username=mysql_real_escape_string($_COOKIE['userlogin']); $loginhash=$_COOKIE['loginhash']; // i guess you should not use mysql_real_escape_string $salt=mysql_real_escape_string(substr($loginhash,-8)); // here would be the place to use it $result=mysql_query("select * from members where (username='$username' || email='$username') && salt='$salt' limit 1") or die (mysql_error()); $row=mysql_fetch_assoc($result); $cryptedhash=$row['loginhash']; if (password_verify($loginhash, $cryptedhash)){ $_SESSION['username']=$row[username]; $_SESSION['user_id']=$row['id']; } } |
号
此行错误