哈希与安全

PHP password_hash()生成器

按你选择的算法和成本参数运行PHP的password_hash()。

给这个工具评分

如何使用 PHP password_hash()生成器

  1. 输入你想哈希的密码。
  2. 选择算法——PASSWORD_DEFAULT和PASSWORD_BCRYPT会使用成本参数;Argon2选项会忽略它。
  3. 点击运行password_hash(),复制生成的哈希值。

关于 PHP password_hash()生成器

这就是PHP自身的password_hash()函数,在服务器上按你代码中运行时完全相同的方式执行。通过PHP使用的同一套常量选择算法——PASSWORD_DEFAULTPASSWORD_BCRYPTPASSWORD_ARGON2IPASSWORD_ARGON2ID——设置一个成本参数,你就能得到函数实际生成的精确字符串,以及password_get_info()报告的详细分解信息。无需搭建脚本环境,就能快速看到某个密码和某组设置实际生成的结果。

这里生成的每一个哈希都有两点值得理解。第一,输出会自动加盐,因此对同一个密码运行两次会得到两个不同的哈希值——这是正确行为,不是bug,也正因如此,你绝不能通过重新哈希来比较两个哈希值。正确的验证方式是使用password_verify($password, $hash),它会从存储的哈希中读取盐值和成本参数,并自动完成比对。第二,成本参数(bcrypt)以及Argon2的内存和时间参数都是刻意设计得很慢的:它们决定了攻击者每猜一次密码需要付出多大代价,因此成本越高越安全,但计算耗时也越长。这个工具把bcrypt的成本上限设为12,并对Argon2使用固定参数,以防止公开接口被用来消耗大量CPU资源。

到目前为止,PASSWORD_DEFAULT在每个PHP版本中都是bcrypt,但它的定义是"PHP当前认为最佳的算法",因此未来版本中可能会发生变化——这正是为什么算法信息会存储在哈希值内部,交由password_verify()自行判断处理。对于大多数新项目而言,Argon2id是目前的最佳实践。还需注意,bcrypt会悄悄忽略密码前72字节之后的内容,因此这个工具会拒绝更长的密码,而不是对截断后的版本进行哈希。想验证哈希而不是生成哈希?可以使用bcrypt与Argon2验证工具,或者用Argon2生成器对比不同算法。密码在哈希完成后会立即丢弃——绝不会被记录或存储。

常见问题

我应该选择哪种算法?

对于新项目,如果你的PHP版本支持,PASSWORD_ARGON2ID是目前的最佳实践。PASSWORD_DEFAULT也是一个安全、通用的选择——目前它就是bcrypt,PHP会随时间推移升级它。单纯使用PASSWORD_BCRYPT也没问题,且所有版本都支持。

为什么同一个密码每次生成的哈希都不一样?

因为password_hash()每次调用都会生成一个全新的随机盐值,并把它存储在输出结果内部。这正是你想要的效果——它能防止两个相同的密码生成相同的哈希值。切勿通过重新哈希来比较密码,应该使用password_verify()。

成本参数的作用是什么?

对于bcrypt(以及PASSWORD_DEFAULT)而言,它决定了每次哈希需要经过多少轮运算——每增加1,耗时大致翻倍。数值越高,越能抵御暴力破解,但计算也越慢。Argon2选项会忽略这个成本参数,改用固定的内存和时间参数。

之后我该怎么用这些哈希值来验证密码?

调用password_verify($password, $hash)。它会从存储的哈希值中读取算法、盐值和成本参数,并返回true或false。你不需要单独记录或存储算法信息——它已经包含在哈希字符串内部了。

在这里哈希真实密码安全吗?

密码会被发送到服务器,完成哈希后立即丢弃——不会被记录或存储,响应中也带有no-store响应头。即便如此,对于生产环境中实际使用的密码,在你自己的环境中生成哈希始终是更稳妥的做法。