Coda Hale 的文章“如何安全存储密码”声称:
bcrypt 具有内置的盐,可以防止彩虹表攻击。
他引用了这篇论文,该论文说在 OpenBSD 的bcrypt
实现中:
OpenBSD 从 arcfour(arc4random(3))密钥流生成 128 位 bcrypt 盐,该密钥流以内核从设备时序收集的随机数据作为种子。
我不知道这是怎么工作的。在我的盐概念中:
当我将 Devise(Rails 登录管理器)与 bcrypt 一起使用时,数据库中没有 salt 列,所以我很困惑。如果盐是随机的并且没有存储在任何地方,我们如何可靠地重复哈希过程?
简而言之, bcrypt 如何具有内置的盐?
这是 bcrypt:
产生随机盐。已经预先配置了 “成本” 因素。收集密码。
使用盐和成本因数从密码中得出加密密钥。用它来加密一个众所周知的字符串。存储费用,盐和密文。由于这三个元素的长度已知,因此很容易将它们连接起来并存储在一个字段中,但是以后可以将它们分开。
当有人尝试进行身份验证时,请取回存储的成本和费用。从输入的密码,费用和盐中得出一个密钥。加密相同的知名字符串。如果生成的密文与存储的密文匹配,则密码为匹配项。
Bcrypt 与基于 PBKDF2 之类的算法的传统方案非常相似。主要区别在于它使用派生密钥对已知的纯文本进行加密。其他方案(合理地)假定密钥派生函数是不可逆的,并直接存储派生密钥。
存储在数据库中的bcrypt
“哈希” 可能看起来像这样:
$ 2a $ 10 $ vI8aWBnW3fID.ZQ4 / zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa
这实际上是三个字段,以 “$” 分隔:
2a
标识使用的bcrypt
算法版本。10
是成本因素; 2 使用了10 个密钥派生函数的迭代(顺便说一句,这还不够。我建议花费 12 或更多)。vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa
是盐和密文,在修改后的 Base-64 中进行连接和编码。前 22 个字符解码为 salt 的 16 字节值。其余字符是要进行身份验证的密文。我认为该措词的措词应如下:
bcrypt 在生成的哈希表中内置了盐,以防止彩虹表攻击。
bcrypt
实用程序本身似乎并不维护盐列表。相反,盐是随机生成的,并附加到函数的输出中,以便以后可以记住它们(根据bcrypt
的 Java 实现)。 bcrypt
生成的 “哈希”不仅仅是哈希。相反,它是哈希和盐串联在一起的。
为了使事情更加清晰,
密码 + salt 使用由以下各项生成的密钥加密:成本,盐和密码。我们称该加密值为cipher text
。然后将盐附加到该值,并使用 base64 对其进行编码。附加成本,这是bcrypt
产生的字符串:
$2a$COST$BASE64
该值最终被存储。
如果攻击者获得了对数据库的控制权,则攻击者将轻松解码 base64 值,然后他就能看到盐。盐不是秘密。虽然是随机的。然后,他将需要解密cipher text
。
更重要的是:此过程中没有散列,而是 CPU 昂贵的加密 - 解密。因此,彩虹表在这里的意义不大。