如果您从新密码而不是旧密码生成变体,这很容易做到。
想象一个算法,它接受一个密码,并生成数百个简单的变体。因此,对于这样的输入,password1
它会生成以下变体:
PASSWORD1
PasSSword1
password2
password
P@aaW0RD2
....
和其他几百人。(请注意,这些变体之一是"password"
。)
密码破解者拥有类似的算法,并使用它们对单词字典进行猜测。
使用该算法,我们可以执行以下步骤:
用户将他们的第一个密码设置为"password"
。系统存储hash("password")
,并在用户登录时使用此哈希进行比较。
几周后,用户将密码更改为"hunter2"
. 当前密码哈希更改为hash("hunter2")
. hash("password")
存储在 N 个历史密码哈希列表中。
几周后,用户尝试将密码更改为"password1"
. 在此更改尝试期间,将执行以下步骤。
3a) 新密码是明文可用的,因为用户刚刚输入了它。"password1"
用作上述变体生成算法的输入。生成了数百个不同的密码(明文)。
hash(variant)
3b) 对于每个变体密码,使用每个旧密码的盐值计算该密码的哈希值。如上所述, 的变体之一"password1"
是"password"
,因此在我们的变体哈希列表中,我们将拥有hash("password")
。我们最终使用 V 变体哈希来测试每个旧密码,总共 V*N 哈希。
3c)针对当前散列和先前散列测试每个变体散列的正确加盐版本,以进行总共 V * N 二进制数组比较。
3d) 变体哈希hash("password")
之一是 并且存储的 N 个历史哈希之一也是hash("password")
。这些哈希完全匹配,因此密码被拒绝。
该技术需要生成 V*N 哈希,其中 V 约为 500,N 约为 10。这只需几秒钟(即使使用 PBKDF2、bcrypt 或 scrypt),并且无需访问磁盘即可轻松执行。
关于盐的注意事项:每当您更改用户密码时更改盐(您绝对应该这样做)确实会使事情变得更加困难,但您仍然只需要生成线性数量的哈希,无论您的盐有多大。