bcrypt怎么会有内置的盐?

Coda Hale的文章“如何安全地存储密码”声称:

bcrypt内置了盐来防止彩虹表攻击。

他引用了本文,它说在OpenBSD的bcrypt实现中:

OpenBSD生成一个128位的bcrypt盐 (ARC4RANK(3))密钥流,用随机数据播种内核 从设备定时收集。

我不明白这是怎么回事。在我对盐的概念中:

  • 每个存储的密码都需要不同,因此必须为每个密码生成一个单独的彩虹表
  • 它需要存储在某个地方,以便它是可重复的:当用户尝试登录时,我们尝试他们的密码,重复我们最初存储密码时所做的相同的盐和哈希过程,并比较

当我将Devise(一个Rails登录管理器)与bcrypt一起使用时,数据库中没有盐列,所以我很困惑。如果盐是随机的并且没有存储在任何地方,我们如何可靠地重复哈希过程?

简而言之,bcrypt怎么会有内置的盐

193453 次浏览

我认为这句话应措辞如下:

bcrypt有盐内置到生成的哈希中来防止彩虹表攻击。

bcrypt实用程序本身似乎并没有维护盐列表。相反,盐是随机生成的,并附加到函数的输出中,以便以后记住它们(根据bcrypt的Java实现)。换句话说,bcrypt生成的“哈希”不是哈希只是。相反,它是盐连接的哈希

这是bcrypt:

生成随机盐。“成本”因子已预先配置。收集密码。

使用盐和成本因子从密码中导出加密密钥。用它来加密一个众所周知的字符串。商店成本,盐,和密文。因为这三个元素的长度是已知的,所以很容易将它们连接起来并存储在一个字段中,但以后可以将它们拆分。

当有人试图进行身份验证时,检索存储的成本和盐。从输入密码、成本和盐中导出密钥。加密相同的众所周知的字符串。如果生成的密文与存储的密文匹配,则密码匹配。

Bcrypt的操作方式与基于PBKDF2等算法的更传统的方案非常相似。主要区别在于它使用派生密钥来加密已知的纯文本;其他方案(合理地)假设密钥派生函数是不可逆的,并直接存储派生密钥。


存储在数据库中,bcrypt“哈希”可能看起来像这样:

$2a10$$vI8aWBnW3fID.ZQ4/zo 1G. q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

这实际上是三个字段,由“$”分隔:

  • 2a标识使用的bcrypt算法版本。
  • 10是成本因子;使用210密钥推导函数迭代(顺便说一句,这还不够。我建议12或更多的成本。)
  • vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa是盐和密文,在修改后的Base-64中连接和编码。前22个字符解码为盐的16字节值。其余字符是要进行身份验证比较的密文。

此示例取自留档Coda Hale的ruby实现。

为了让事情更清楚,

注册/登录方向->

密码+盐是用从:成本、盐和密码生成的密钥加密的。我们称该加密值为cipher text。然后我们将盐附加到此值并使用base 64对其进行编码。将成本附加到它,这是从bcrypt产生的字符串:

$2a$COST$BASE64

这个值最终会被存储。

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

如果攻击者控制了数据库,攻击者将轻松解码bas64值,然后他将能够看到盐。盐不是秘密的。虽然它是随机的。 然后他需要解密cipher text

更重要的是:这个过程中没有哈希,而是CPU昂贵的加密-解密。因此彩虹表在这里不太相关。

这是一个简单的术语…

Bcrypt没有数据库,它存储盐…

盐以bas64格式添加到哈希中。

问题是bcrypt在没有数据库的情况下如何验证密码…?

bcrypt所做的是从密码哈希中提取盐……使用提取的盐来加密普通密码,并将新哈希与旧哈希进行比较,看看它们是否相同……

让我们想象一个有1个哈希密码的表。如果黑客获得访问权限,他会知道盐,但他必须计算所有常见密码的大列表,并在每次计算后进行比较。这需要时间,他只会破解1个密码。

想象一下同一个表中的第二个哈希密码。盐是可见的,但上述相同的计算需要再次发生才能破解这个,因为盐是不同的。

如果不使用随机盐,它会容易得多,为什么?如果我们使用简单的哈希,我们可以为常见密码生成1次哈希(彩虹表),只需进行简单的表搜索,或者在数据库表哈希和我们预先计算的哈希之间进行简单的文件搜索即可找到普通密码。