使用PHP 5.5的password_hash和password_verify函数

时间:2013-02-21 00:11:00

标签: php mysql hash passwords php-password-hash

假设我想为用户存储密码,这是使用PHP 5.5的password_hash()函数(或PHP 5.3.7+的此版本:https://github.com/ircmaxell/password_compat的正确方法吗? )?

$options = array("cost" => 10, "salt" => uniqid());
$hash = password_hash($password, PASSWORD_BCRYPT, $options);

然后我会这样做:

mysql_query("INSERT INTO users(username,password, salt) VALUES($username, $hash, " . $options['salt']);

插入数据库。

然后验证:

$row = mysql_fetch_assoc(mysql_query("SELECT salt FROM users WHERE id=$userid"));
$salt = $row["salt"];
$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 10, "salt" => $salt));

if (password_verify($password, $hash) {
    // Verified
}

4 个答案:

答案 0 :(得分:59)

暂时忽略数据库语句的问题,我将回答有关password_hash的问题。

简而言之,不,那不是你怎么做的。您不希望单独存储salt,您应该同时存储hash和salt,然后使用它们来验证密码。 password_hash返回包含两者的字符串。

password_hash函数返回一个包含哈希值和salt的字符串。所以:

$hashAndSalt = password_hash($password, PASSWORD_BCRYPT);
// Insert $hashAndSalt into database against user

然后验证:

// Fetch hash+salt from database, place in $hashAndSalt variable
// and then to verify $password:
if (password_verify($password, $hashAndSalt)) {
   // Verified
}

此外,正如评论所示,如果您对安全性感兴趣,可能需要查看mysqli(PHP5.5中不推荐使用ext/mysql),以及有关SQL注入的文章: http://php.net/manual/en/security.database.sql-injection.php

答案 1 :(得分:11)

建议不要使用自己的盐,从PHP 7开始,its use is deprecated。为了理解原因,password_hash的作者分享了这些想法(链接已解散)

  

有一件事我已经非常清楚了:盐的选择是   危险的。我还没有看到盐选项的单一用法   甚至还不错。每个用法都来自坏(传递mt_rand()   输出)到危险(静态字符串)疯狂(传递密码   作为自己的盐)。

     

我得出的结论是,我认为我们不应该允许用户   指定盐。

他甚至made this comment in SO chat注意到你自己的盐有多糟糕

答案 2 :(得分:7)

请注意来自php.net

  

警告

     

自PHP 7.0.0起,salt选项已被弃用。就是现在   首选只使用默认生成的盐。

结论?忘记盐的选择。

这足够password_hash('password', PASSWORD_DEFAULT) *(或_BCRYPT)

答案 3 :(得分:5)

你不应该输入自己的盐,把盐留空,功能会产生很好的随机盐。

将整个函数返回的字符串插入数据库(或文件或任何您使用的)中。它包含: 算法,成本,盐(22个字符)和哈希密码的ID。

整个字符串都需要使用password_verify()。盐是随机的,不会伤到落入坏人手中(使用哈希密码)。这可以防止(或非常困难)使用ready set生成的密码和散列列表 - 彩虹表。

您应该考虑添加费用参数。默认(如果省略)是10 - 如果更高则函数计算哈希更长。将成本增加1,生成哈希所需的时间翻倍(从而延长了破解密码所需的时间)

$hash = password_hash($password, PASSWORD_BCRYPT, array("cost" => 10));

您应该根据服务器上的速度检查设置此参数。建议功能执行100ms +(有些人更喜欢使其达到250毫秒)。通常成本= 10或11是一个不错的选择(2015年)。

为了提高安全性,您可能希望将密码添加到密码长(50-60个字符是不错的选择)秘密字符串。在使用password_hash()或password_verify()之前。

$secret_string = 'asCaahC72D2bywdu@#$@#$234';
$password  = trim($_POST['user_password']) . $secret_string;
// here use password_* function
  

注意   对algo参数使用PASSWORD_BCRYPT将导致密码参数被截断为最大长度为72个字符。

如果$ password将超过72个字符并且您更改或添加73或90个字符,则哈希不会更改。可选,坚持$ secret_string应该在最后(在用户密码之后而不是之前)。