协慌网

登录 贡献 社区

bcrypt 如何内置盐?

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 字节值。其余字符是要进行身份验证的密文。

本示例摘自Coda Hale 的 ruby 实现文档。

我认为该措词的措词应如下:

bcrypt 在生成的哈希表中内置了盐,以防止彩虹表攻击。

bcrypt实用程序本身似乎并不维护盐列表。相反,盐是随机生成的,并附加到函数的输出中,以便以后可以记住它们(根据bcrypt的 Java 实现)。 bcrypt生成的 “哈希”不仅仅是哈希。相反,它是哈希盐串联在一起的。

为了使事情更加清晰,

注册 / 登录方向 ->

密码 + salt 使用由以下各项生成的密钥加密:成本,盐和密码。我们称该加密值为cipher text 。然后将盐附加到该值,并使用 base64 对其进行编码。附加成本,这是bcrypt产生的字符串:

$2a$COST$BASE64

该值最终被存储。

攻击者需要做什么才能找到密码? (其他方向 <-)

如果攻击者获得了对数据库的控制权,则攻击者将轻松解码 base64 值,然后他就能看到盐。盐不是秘密。虽然是随机的。然后,他将需要解密cipher text

更重要的是:此过程中没有散列,而是 CPU 昂贵的加密 - 解密。因此,彩虹表在这里的意义不大。