php 中的 password_hash/password_verify 是如何工作的?

信息安全 哈希 php
2021-08-15 02:41:28

为什么PHP 手册中的这个示例每次运行时都会给出不同的结果?

echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT);

然后,password_verify() 知道所有这些哈希匹配“rasmuslerdorf”!即使文档清楚地说明,这对我来说就像魔术一样:

请注意,password_hash() 作为返回哈希的一部分返回算法、成本和盐。因此,验证哈希所需的所有信息都包含在其中。这允许验证函数验证哈希,而无需单独存储盐或算法信息。

此功能可安全抵御定时攻击。

echo password_verify ( 'rasmuslerdorf' , '$2y$10$EMawXU7qNS4GzU2Do8bByeb7sSQZxecvmZ6mBrToxsOaY7RMAIGua' );  //=>true
echo password_verify ( 'rasmuslerdorf' , '$2y$10$0vMA2k7LxTBstI/J7clkkuZZ/XtuS1fklVuoM6sl4Fc/aj1avQa5u' ); //=>true
echo password_verify ( 'rasmuslerdorf' , '$2y$10$iuE2EzHMNONAWFKh/4Wyl.dcBxgFaNzAh32va0/gyE4ScqnNr/Uc.' ); //=>true

到底是怎么回事?password_verify() 如何知道一些疯狂的字符串匹配 'rasmuslerdorf' 而黑客却不知道?

2个回答

password_hash函数在内部执行以下步骤:

  1. 每次您调用它时,它都会随机选择一个新鲜的盐。
  2. 它应用了一个代价高昂的散列函数,该函数将随机盐、密码和其他算法参数(例如,成本因素)作为输入。
  3. 它将算法参数、随机盐和哈希输出组合成一个输出字符串,可以对其进行解析以单独恢复它们。

#1中的随机选择是即使您提供相同的输入,它每次都会产生不同输出的原因。第 3 步的格式化输出允许password_verify知道由password_hash.

创建有用的密码散列的关键是算法是不可逆的,但始终是可重复的。那是:

  • 仅给出 的输出password_hash('rasmuslerdorf'),您无法取回字符串'rasmuslerdorf'
  • 给定输入'rasmuslerdorf',您可以生成与先前调用相同的输出password_hash('rasmuslerdorf')

这允许您根据哈希检查用户的密码尝试,而无需检索他们的密码。

获得可重复哈希的最简单方法是让它只依赖于输入——因此,它password_hash('rasmuslerdorf')总是返回相同的值。但这意味着攻击者可以计算一堆常见密码的哈希值,并在被盗数据库中搜索匹配项。

因此,一个好的哈希算法会添加一个salt,它只是添加到密码中的一个随机字符串,以使哈希每次都不同。为了稍后重复哈希函数并得到相同的答案,您需要知道存储时使用了哪种盐。

这是password_hash实际输出的内容,合并为一个字符串:

  • 使用特定盐对输入进行哈希处理的结果
  • 使用的随机盐
  • 使用的哈希算法
  • 控制散列算法的任何其他选项(例如,用于故意减慢散列的轮数)

这个输出每次都会不同,所以我们需要一个不同的函数来重复用户输入的哈希,看看我们是否得到相同的答案;password_verify是为了:

  • 从存储的字符串中,找到算法、选项和盐
  • 使用这些参数和用户尝试登录的密码运行哈希算法
  • 检查结果是否与存储字符串的哈希部分匹配

如果使用相同的选项和哈希运行相同的算法会导致不同的结果,则用户一定输入了错误的密码。