bcrypt如何内置盐?

Nat*_*ong 570 security hash internals bcrypt

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

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

他引用了这篇论文,其中说在OpenBSD的实现中bcrypt:

OpenBSD从arcfour(arc4random(3))密钥流生成128位bcrypt salt,并使用内核从设备时序收集的随机数据进行种子处理.

我不明白这是如何工作的.在我的盐概念中:

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

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

简而言之,bcrypt如何内置盐

eri*_*son 733

这是bcrypt:

生成随机盐.已经预先配置了"成本"因素.收集密码.

使用salt和cost因子从密码派生加密密钥.用它来加密一个众所周知的字符串.存储成本,和密文.因为这三个元素具有已知的长度,所以很容易将它们连接起来并将它们存储在单个字段中,但是稍后可以将它们分开.

当有人尝试进行身份验证时,检索存储的成本和盐.从输入密码,成本和盐中获取密钥.加密相同的知名字符串.如果生成的密文与存储的密文匹配,则密码匹配.

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


存储在数据库中的bcrypt"哈希"可能如下所示:

$ 2A $ 10 $ vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa

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

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

这个例子来自Coda Hale的ruby实现文档.

  • bcrypt的成本因子是指数的,或者更确切地说,成本因子10表示2 ^ 10轮(1024),成本因子16表示2 ^ 16轮(65536).这很自然,需要5-10秒.它应该花费大约64倍的成本因子10.为了清除其他错误信息,PHP的crypt函数使用在c中实现的unix crypt库. (55认同)
  • @LobsterMan不,不是真的.如果你可以保守秘密,你不会使用这种方法,你只需存储密码.密码身份验证方案基于攻击者已发现您知道的所有内容的假设.盐需要单独攻击每个密码.测试密码所需的计算工作量由迭代决定.如果用户选择了良好的密码,即使暴露出盐,它们也是安全的.在某些情况下,隐藏盐可以帮助密码错误的人,但我会首先考虑密码质量. (7认同)
  • 您是否要详细了解为何成本因子10不够?在Grails中,我注意到bcrypt的成本因子/对数回合的默认值为10,因此根据您的建议可能值得更新。 (4认同)
  • @TJChambers 没错;如果您可以在帐户上设置密码,您将能够进行身份验证。密码散列不是为了防止这种攻击。它旨在防止对密码表具有只读访问权限的攻击者进行身份验证。例如,您会得到一个带有桌子的备份磁带。 (3认同)
  • 用削片机存放盐不是很安全吗?如果有人掌握了哈希,只要有足够的计算,它就可以被破解。如果他不知道盐,这几乎是不可能的。 (2认同)
  • @NLV 这是 bcrypt 规范中定义的字符串:`“OrpheanBeholderScryDoubt”` (2认同)

Ada*_*ter 171

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

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

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

  • @Nathan:`bcrypt`知道如何从生成的输出中提取盐(存储在数据库中).当需要进行身份验证时,`bcrypt`会将原始输出分成哈希和salt组件.salt组件应用于用户键入的传入密码. (43认同)
  • 要回答Nathan Long的评论,一个很好的思考方式就是盐并不是秘密.这就是为什么盐被包含在bcrypt函数的输出中作为上面指出的答案之一.盐是为了防止彩虹表,这是常见密码的列表,或只是暴力等等...不同的密码,但哈希.如果没有salt,数据库A中密码的哈希值将与数据库B中密码的哈希值相同.Salt只会更改哈希值,这使得窃取数据库的人更难解密(解除密码)密码. (18认同)
  • 好的,所以我注册一个网站并选择密码"foo".`Bcrypt`添加了一个"akd2!*"的随机盐,导致"fooakd2!*",它被散列并存储.后来,我尝试使用密码"bar"登录.要看我是否正确,它需要哈希"barakd2!*".如果盐是随机生成的,那么它如何在散列和比较之前知道如何将它添加回"bar"? (17认同)
  • @Nathan但攻击者可以删除所有密码中的已知盐,然后用它们创建一个表吗? (10认同)
  • 这就是我的理解:这个想法是每个密码都有一个独特的盐。盐包含在密码哈希中,因此黑客必须为每个密码创建一个彩虹表。对于中等数据库而言,这将花费大量时间。这一切都是为了减缓攻击者的速度,从而使暴力破解变得毫无意义。 (3认同)
  • 是什么阻止了黑客从哈希中删除盐,只是将他的无盐哈希与它进行比较? (3认同)
  • @KevinFrostad 您找到了密码哈希列表。“123456”是一个非常常见的密码,您生成它的哈希值,对每个条目进行尝试:即时访问一堆帐户。使用盐,所有蹩脚的“123456”实际上都会被更改为带有唯一盐的前缀(纯文本密码,而不是哈希值)。您现在需要为每个条目生成 SHA1 哈希值(“ak5t123456”、“j3rt123456”、“7w8d123456”)。您需要更多时间来访问同一组帐户,从而使服务有更多时间来发现违规行为并做出反应。 (2认同)
  • @KevinFrostad 知道盐的长度并不会以任何有意义的方式减少工作量。您仍然必须单独攻击每个密码。并且每次成功的攻击都独立于下一次攻击。破解一个弱密码并没有任何好处,你只是破解了一个弱密码。 (2认同)

小智 19

这是一个简单的术语...

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

盐以 Base64 格式添加到哈希中......

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

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


jon*_*y89 5

为了让事情变得更清楚,

注册/登录方向->

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

$2a$COST$BASE64

该值最终被存储。

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

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

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