bcrypt 怎么可能有内置的盐?

How can bcrypt have built-in salts?

提问人:Nathan Long 提问时间:7/26/2011 最后编辑:brian d foyNathan Long 更新时间:6/13/2023 访问量:208123

问:

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

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

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

OpenBSD 从 arcfour 生成 128 位 bcrypt salt (arc4random(3)) 密钥流,在内核中植入随机数据 从设备计时收集。

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

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

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

简而言之,bcrypt 如何具有内置盐?

安全 bcrypt

评论


答:

206赞 Adam Paynter 7/26/2011 #1

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

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

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

评论

34赞 Nathan Long 7/26/2011
好的,所以我注册了一个网站并选择密码“foo”。 添加“akd2!*”的随机盐,得到“fooakd2!*”,该盐被散列并存储。稍后,我尝试使用密码“bar”登录。要查看我是否正确,它需要散列“barakd2!*”。如果盐一开始是随机生成的,那么在散列和比较之前,它如何知道如何将其添加回“条形图”?Bcrypt
57赞 Adam Paynter 7/26/2011
@Nathan:知道如何从生成的输出(存储在数据库中)中提取盐。当需要进行身份验证时,将原始输出分成哈希和盐组件。salt 组件应用于用户键入的传入密码。bcryptbcrypt
33赞 Joseph Astrahan 1/6/2016
为了回答Nathan Long的评论,一个很好的思考方式是,盐并不意味着秘密。这就是为什么盐包含在 bcrypt 函数的输出中的原因,作为上面指出的答案之一。盐的存在是为了防止彩虹表,彩虹表是常用密码列表,或者只是蛮力等......不同的密码,但经过哈希处理。如果没有 salt,数据库 A 中密码的哈希值将与数据库 B 中密码的哈希值相同。
12赞 Oscar 8/12/2016
@Nathan但是,攻击者是否可以删除所有密码中的已知盐,然后使用它们创建表?
6赞 Kevin Frostad 9/11/2018
是什么阻止了黑客仅仅从哈希中删除盐,而只是将他的未加盐哈希与它进行比较?
957赞 erickson 7/27/2011 #2

这是 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 实现文档。

评论

7赞 pm_labs 5/15/2012
您能否详细说明为什么 10 的成本系数不够?在 Grails 中,我注意到 10 是 bcrypt 的成本因子/对数轮次的默认值,因此根据您的建议,它可能值得更新。
62赞 thomasrutter 7/3/2012
bcrypt 的成本因子是指数级的,或者更确切地说,成本因子 10 意味着 2^10 轮 (1024),成本因子 16 意味着 2^16 轮 (65536)。很自然地需要 5-10 秒。它所花费的时间大约是成本系数 10 的 64 倍。为了清除其他错误信息,PHP 的 crypt 函数使用了用 c 语言实现的 unix crypt 库。
4赞 erickson 8/7/2014
@TJChambers 没错;如果您可以在帐户上设置密码,您将能够进行身份验证。密码哈希并非旨在防止这种攻击。它旨在防止对密码表具有只读访问权限的攻击者进行身份验证。例如,您会得到一个带有表的备份磁带。
3赞 LobsterMan 1/2/2018
用凿子储存盐不是安全性不好吗?如果有人掌握了哈希值,通过足够的计算,它可以被破解。如果他不知道盐,那几乎是不可能的。
19赞 erickson 1/2/2018
@LobsterMan 不,不是真的。如果你可以保守秘密,你就不会使用这种方法,你只会存储密码。密码身份验证方案基于攻击者已发现您所知道的一切的假设。盐的存在是要求单独攻击每个密码。测试密码所需的计算工作量由迭代控制。如果用户选择好的密码,即使盐被泄露,它们也是安全的。在某些情况下,隐藏盐可以帮助密码错误的人,但我会首先研究密码质量。
5赞 jony89 6/7/2020 #3

为了让事情更清楚,

注册/登录方向 ->

密码 + salt 使用从以下位置生成的密钥进行加密:cost、salt 和密码。我们将该加密值称为 .然后我们将盐附加到这个值上,并使用 base64 对其进行编码。将成本附加到它,这是从中生成的字符串:cipher textbcrypt

$2a$COST$BASE64

此值最终会存储。

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

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

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

18赞 Manomite 5/3/2021 #4

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

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

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

问题是 bcrypt 在没有数据库时如何验证密码......?

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

评论

0赞 Ketan Patil 8/1/2022
非常简单但准确的解释。谢谢@manomite。
0赞 NickW 1/27/2023
这应该有更多的选票。
0赞 quangkid 11/25/2023
“It extract the salt from the password hash” : 我们如何提取它?它在哈希中是否有固定的长度和固定位置?
0赞 Khalifa 10/18/2021 #5

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

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

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