33

假设我想为用户存储密码,这是否是使用 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

4 回答 4

59

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

简而言之,不,这不是你的做法。您不想单独存储盐,您应该同时存储哈希和盐,然后使用两者来验证密码。password_hash返回一个包含两者的字符串。

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

$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
}

此外,正如评论所暗示的,如果您对安全性感兴趣,您可能需要查看mysqliext/mysql在 PHP5.5 中已弃用),还有这篇关于 SQL 注入的文章:http: //php.net/manual/en/security .database.sql-injection.php

于 2013-02-21T00:28:04.590 回答
11

不推荐使用你自己的 salt,从 PHP 7 开始,它的使用已被弃用。为了理解原因,作者password_hash分享了这些想法(链接已失效)

我已经非常清楚一件事:盐的选择很危险。我还没有看到盐选项的单一用法甚至还不错。每种用法的范围从糟糕(传递 mt_rand() 输出)到危险(静态字符串)到疯狂(将密码作为自己的盐传递)。

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

他甚至在 SO 聊天中发表了这条评论,指出通过自己的盐是多么糟糕

于 2015-09-14T22:44:41.603 回答
7

请注意 php.net

警告

自 PHP 7.0.0 起,盐选项已被弃用。现在首选简单地使用默认生成的盐。

结论?忘记盐的选择。

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

于 2015-09-03T01:01:31.740 回答
5

你不应该输入自己的盐,把盐留空,函数会生成好的随机盐。

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

使用password_verify()需要整个字符串。盐是随机的,落入坏人之手(使用散列密码)不会造成伤害。这会阻止(或非常困难)使用现成的集合生成的密码和哈希列表 - 彩虹表。

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

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

您应该根据服务器上的速度检查设置此参数。建议该函数执行 100 毫秒以上(有些人更喜欢将其设为 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

警告 将 PASSWORD_BCRYPT 用于 algo 参数,将导致密码参数被截断为最大长度 72 个字符。

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

于 2015-06-23T22:39:05.540 回答