【问题标题】:How do I use the Argon2 algorithm with password_hash?如何将 Argon2 算法与 password_hash 一起使用?
【发布时间】:2021-07-22 23:57:08
【问题描述】:

所以我听说 PHP 7.2 引入了新的Argon2 algorithm。但是我对如何将它与现有代码一起使用感到困惑。比如我有这个

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

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

【问题讨论】:

    标签: php algorithm php-password-hash php-7.2 argon2-ffi


    【解决方案1】:

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

    在 PHP 7.2 之前,唯一使用的散列算法 password_hash 是 bcrypt。在撰写本文时,bcrypt 仍然被认为是一种强哈希,尤其是与其前身md5sha1(两者都是insecure because they are fast)相比。 Argon2 是simply a costlier algorithm to brute force

    Argon2i 使用与数据无关的内存访问。它比较慢,因为它在内存上进行了更多的传递以防止权衡攻击。强烈建议用于密码哈希和基于密码的密钥派生。

    Bcrypt 仍然是可接受的密码哈希。如果您不想切换,则无需切换(从 7.2.0 版本开始)。此外,PASSWORD_DEFAULT 应该只在下一个完整版本(7.3.0 或更高版本)时更改(根据PHP Internals policy)。如果您想确保仅继续使用 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]);
    

    From the manual 我们看看这些选项的作用

    • memory_cost - 可用于计算 Argon2 哈希的最大内存(以千字节为单位)(默认为 1024)
    • time_cost - 计算 Argon2 哈希可能花费的最长时间(默认 2)
    • threads - 用于计算 Argon2 哈希的线程数(默认 2)

    了解,在您更改这些之前,此处较高的成本会减慢您的脚本。您需要在服务器上运行测试以找到最适合您的设置。这通常是通过循环给定成本的几次迭代。 PHP manual gives an example of this 如果你需要的话。

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

    我们在password_verify中更改了什么?

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

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

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

    什么是 Argon2ID?

    Introduced in PHP 7.3,Argon2ID 对 Argon2I 做了一些改进,如 Crypto.SE question 中所述

    对 1-pass Argon2id 的最佳权衡攻击是组合低存储攻击(针对内存的前半部分)和排名攻击(针对后半部分),它们的因子约为 2.1。

    Argon2ID 使用与 Argon2I 相同的参数。

    【讨论】:

    • 在这个答案中添加如何将 Argon2 加载到 PHP 运行时模块中会很有用。你的回答暗示 PHP 7.2 默认有它,而我只能在我的设置中显示 PHP 7.1 没有,干杯
    • @Martin 没有要加载的模块。如果您运行的是 7.2.0 或更高版本,它就在那里(password_hashcore PHP)。如果您是这个意思,我不知道有任何反向移植选项。
    • 与其检查字符串的开头,不如使用php.net/manual/en/function.password-needs-rehash.php 作为“此函数检查提供的哈希是否实现了提供的算法和选项”
    • @IrfandyJip 两者。请记住,password_verify 采用哈希和要验证的密码的所有选项并生成一个新的哈希。如果新哈希(具有相同的选项)与旧哈希匹配,则验证密码。
    • @SilverSurfer 不需要费用。如果不提供值,PHP 使用默认值
    【解决方案2】:

    仅当您使用 PHP 7.3 时: 我创建了 2 个简单而纤细的函数来将 Argon2ID 与 PHP 一起使用:

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

    获取一个哈希值使用(最后一个参数是可选的,你可以选择hex, base64 或者什么都不用)[return => string]:

    $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 是安全的,我们将不得不在几年后看到它会是多少。对于否决您的动机,我认为它无效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 1970-01-01
    • 2020-05-14
    • 1970-01-01
    • 2019-10-05
    • 1970-01-01
    相关资源
    最近更新 更多