如何在password_hash中使用Argon2算法?

36

我听说PHP 7.2引入了新的Argon2算法。但是我不知道如何将其与我的现有代码一起使用。例如,我有以下代码:

$password = password_hash('somepassword', PASSWORD_DEFAULT, ['cost' => 12]);

PASSWORD_DEFAULT现在是否使用Argon2?如果需要更改password_verify,我需要做什么?bcrypt现在被认为是不安全的吗?

2个回答

68

什么是Argon2?bcrypt现在不好用了吗?

在PHP 7.2之前,password_hash使用的唯一散列算法是bcrypt。截至本文撰写时,bcrypt仍被认为是一种强大的哈希算法,特别是与其前身md5sha1相比(二者都因为速度快而不安全)。Argon2则是一种成本更高的算法,以防止暴力破解(simply a costlier algorithm to brute force)

Argon2i使用数据无关内存访问。它较慢是因为在内存上进行了更多的传递,以保护免受折衷攻击。它非常适合用于密码散列和基于密码的密钥派生。

bcrypt仍然是密码的可接受哈希值。如果你不想换的话,没有必要换(从7.2.0版开始)。此外,根据PHP内部政策PASSWORD_DEFAULT仅会在下一个完整版本(7.3.0或更高版本)中更改。如果您想确保只使用bcrypt,则可以使用PASSWORD_BCRYPT。然而,这是不必要的,因为我们将在下面讨论。

如何使用Argon2?

首先,我们将把password_hash的第二个参数切换到以下其中之一:

  • PASSWORD_ARGON2I - PHP 7.2.0+
  • PASSWORD_ARGON2ID - PHP 7.3.0+(如果有选择则更好,请参见下面的注释)

然后,我们需要更改选项。bcrypt使用cost作为迭代密码多少次的参数(更高的成本=更长的哈希时间)。但是,有不同的成本因素。

password_hash('somepassword', PASSWORD_ARGON2I, ['memory_cost' => 2048, 'time_cost' => 4, 'threads' => 3]);

从手册中 我们可以了解这些选项的含义:

  • memory_cost - 最大内存使用量(以 kibibytes 为单位)用于计算 Argon2 哈希(默认值为 1024)
  • time_cost - 计算 Argon2 哈希所需的最大时间(默认值为 2)
  • threads - 用于计算 Argon2 哈希的线程数(默认值为 2)

在更改这些内容之前,请注意更高的成本 会减慢您的脚本速度。您可以在服务器上运行一个测试,找到最适合您的设置。通常通过循环运行给定成本的多个迭代来实现。如果需要,PHP 手册提供了一个示例

还要注意,虽然 bcrypt 存储 60 个字符,但 Argon2 可能需要更多。您应该理想情况下将密码字段存储为 255 个字符。

password_verify 中我们要更改什么?

答案是... 没有。请注意,password_verify 足够聪明,可以找出使用的算法并适当地处理它。如上所述,这意味着如果您使用 PASSWORD_DEFAULT,默认值可能会更改而不会对您产生负面影响(尽管您可能需要调整成本参数)。password_verify 只需要支持的算法。如果您从 bcrypt 切换到 Argon2,则两者将以相同的方式进行验证,因为所有必需的数据(salt、哈希和成本)都已为您存储。

//Works for both bcrypt and Argon2
if(password_verify($user_password, $stored_hash)) {
    // password validated
}

如果你想升级bcrypt的哈希值,你可以在用户成功登录(因此提供了未哈希的密码)时执行此操作。只需检查哈希是否以$2y$(bcrypt标记)开头。如果是,将提供的密码再次传递给password_hash,但使用Argon2参数,并将其保存到已登录用户的密码字段中。

什么是Argon2ID?

在PHP 7.3中引入的Argon2ID对Argon2I进行了一些改进,如Crypto.SE question所述

针对单次Argon2id的最佳折衷攻击是组合低存储攻击(针对内存的前半部分)和排名攻击(针对后半部分),将约因素结合在一起为2.1倍。

Argon2ID使用与Argon2I相同的参数。


1
在这个答案中加入如何将Argon2加载到PHP运行时模块可能会很有用。你的回答暗示PHP 7.2默认具有它,而我只能在我的设置中显示PHP 7.1 没有,谢谢。 - Martin
1
@Martin,没有可加载的模块。如果您正在运行7.2.0或更高版本,则已经存在(password_hashcore PHP)。如果您的意思是回溯选项,我不知道有任何后移选项。 - Machavity
3
与其检查字符串的起始内容,你应该使用http://php.net/manual/en/function.password-needs-rehash.php作为 "此函数用于检查提供的哈希实现了所提供的算法和选项". - jeroen
1
@IrfandyJip 两者都是。请记住,password_verify 函数会将哈希选项和要验证的密码作为输入,并生成一个新的哈希值。如果新的哈希值(使用完全相同的选项)与旧的哈希值匹配,则密码验证成功。 - Machavity
1
@SilverSurfer 不需要成本。如果您不提供值,PHP将使用默认值。 - Machavity
显示剩余2条评论

1

仅当您使用PHP 7.3时: 我创建了两个简单而精简的函数来使用PHP中的Argon2ID:

function argon2idHash($plaintext, $password, $encoding = null) {
    $plaintextsecured = hash_hmac("sha256", $plaintext, $password);
    return $encoding == "hex" ? bin2hex(password_hash($plaintextsecured, PASSWORD_ARGON2ID)) : ($encoding == "base64" ? base64_encode(password_hash($plaintextsecured, PASSWORD_ARGON2ID)) : password_hash($plaintextsecured, PASSWORD_ARGON2ID));
}

function argon2idHashVerify($plaintext, $password, $hash, $encoding = null) {
    $plaintextsecured = hash_hmac("sha256", $plaintext, $password);
    return password_verify($plaintextsecured, $encoding == "hex" ? hex2bin($hash) : ($encoding == "base64" ? base64_decode($hash) : $hash)) ? true : false;
}

要获取哈希值,请使用以下方法(最后一个参数是可选的,您可以选择hexbase64或不选)[返回 => 字符串]:
$salt = "LALALA";
argon2idHash($clearvalue, $salt, "hex"); // with encoding
argon2idHash($clearvalue, $salt); // without encoding

要验证散列值,请使用(参数$ salt必须与哈希设置的盐匹配,如果使用编码,则同样适用于此规则)[return => bool]:

$salt = "LALALA";
argon2idHashVerify($clearvalue, $salt, $hashtoverify, "hex") ? "match" : "dont match"; // with encoding
argon2idHashVerify($clearvalue, $salt, $hashtoverify) ? "match" : "dont match"; // without encoding

最后,如果你懂PHP,你可以根据自己的喜好修改这些函数,但目前这是我所知道的在数据库中安全存储密码的最佳方式。


我创建的函数唯一的作用是将文本使用您选择的密码或盐进行sha256-hmac加密,然后使用标准argon函数。我的额外步骤提供了额外的安全性。现在你说argon2是安全的,我们将在未来几年看到它有多安全。对于你的负评动机,我不认为它是有效的。 - Marco Concas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接