PHP 的 password_hash() 向后兼容吗?

Is PHP's password_hash() Backwards Compatible?

具体来说,当使用 PASSWORD_DEFAULT 算法时,根据 php page

"Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice)."

"注意:更新此函数支持的算法(或更改默认算法)必须遵循以下规则: 在成为默认算法之前,任何新算法都必须在 PHP 的至少 1 个完整版本中处于核心。因此,例如,如果在 7.5.5 中添加了一种新算法,则直到 7.7(因为 7.6 将是第一个完整版本)才符合默认条件。但是,如果在 7.6.0 中添加了不同的算法,它也可以在 7.7.0 中作为默认值使用。 默认值应该只在完整版本(7.3.0、8.0.0 等)中更改,而不是在修订版本中更改。唯一的例外是在紧急情况下,当在当前默认设置中发现严重的安全漏洞时。"

因此,当将 PHP 升级到对 PASSWORD_DEFAULT 使用不同算法的版本时,如果用户的密码使用旧算法进行哈希处理,是否会阻止用户登录?

是一种单向散列算法,您无法解密散列。

使用password_verify检查密码是否与存储的哈希匹配:

<?php
  $hash = 'your-hash';

  if (password_verify('pass', $hash)) {
    echo 'Password is valid';
  } else {
    echo 'Password is not valid!'; 
  }

简短的回答是肯定的,它是向后兼容的,前提是您的存储允许更长的密码哈希长度。

当您生成密码时,您将获得特定格式的密码。

我使用 bcrypt 和较新的 argon2i 哈希生成了两个密码。 (在 PHP 7.2 中引入 libsodium

仅供参考:我这里的代码基于 libsodium 扩展,因为我还没有下载 php 7.2,并且不打算安装 sodium_compat 只是为了别名。 php 7.2 语法将不包含任何命名空间。

$bcrypt = password_hash('insecurepassword', PASSWORD_BCRYPT);
$argon2i = \Sodium\crypto_pwhash_str('insecurepassword', \Sodium\CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, \Sodium\CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE);

var_dump($bcrypt, $argon2i);

我 运行 时得到的输出是这样的

string(60) "y$jiT0NF3u426kguHes8ZBputRE/n9OSdPi5HhHvEWW4mX1XDwKwy1e"
string(96) "$argon2i$v=19$m=32768,t=4,p=1$Ho4Vzgp5nzQkLlp99P+ViA$bDqX8UUlSnfLRCfFBzBnFhWr/hzHzuuUCfZ0LSIns64"

两个密码的格式大致相同。如果你分解 $ 你最终会得到验证密码所需的每一块拼图。

第一部分包含算法。 2y 对于 bcryptargon2i 对于... argon2i。 (惊喜!)

接下来的位包含配置选项,所以在 bcrypt 的情况下是成本。在 argon2i 的情况下,有一些额外的配置。

最后一部分包含算法要检查的实际密码和盐。

要查看更多视觉细分,请查看 php docs

尽管我使用了一些不同的函数来演示一些输出,但有一个 rfc 被接受,它引入了对 argon2i 散列到 password_hash 函数的支持7.2.

所以当那个时候到来时,password_verify 将 "just work" 无论您是否给它一个 bcryptargon2i 哈希来验证密码。