Coda Hale 的文章 "How To Safely Store a Password" 声称:
bcrypt 内置了盐以防止彩虹表攻击。
他引用了 this paper,它说在 OpenBSD 的 bcrypt
实现中:
OpenBSD 从一个 arcfour (arc4random(3)) 密钥流生成 128 位 bcrypt salt,并使用内核从设备时序收集的随机数据播种。
我不明白这是怎么回事。在我对盐的概念中:
每个存储的密码都需要不同,因此必须为每个密码生成单独的彩虹表
它需要存储在某个地方以便它是可重复的:当用户尝试登录时,我们尝试他们的密码,重复我们最初存储他们的密码时所做的相同的盐和哈希过程,并比较
当我使用带有 bcrypt 的 Devise(Rails 登录管理器)时,数据库中没有 salt 列,所以我很困惑。如果盐是随机的并且没有存储在任何地方,我们如何可靠地重复散列过程?
简而言之,bcrypt 怎么能有内置的盐呢?
这是 bcrypt:
生成随机盐。 “成本”因素已预先配置。收集密码。
使用盐和成本因子从密码中派生加密密钥。用它来加密一个众所周知的字符串。存储成本、盐和密文。因为这三个元素的长度是已知的,所以很容易将它们连接起来并将它们存储在一个字段中,然后可以将它们分开。
当有人尝试进行身份验证时,检索存储的成本和盐。从输入的密码、成本和盐中派生出一个密钥。加密相同的众所周知的字符串。如果生成的密文与存储的密文匹配,则密码匹配。
Bcrypt 的运行方式与基于 PBKDF2 等算法的更传统方案非常相似。主要区别在于它使用派生密钥来加密已知的纯文本。其他方案(合理地)假设密钥派生函数是不可逆的,并直接存储派生密钥。
存储在数据库中的 bcrypt
“哈希”可能如下所示:
$2a$10$vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa
这实际上是三个字段,由“$”分隔:
2a 标识了使用的 bcrypt 算法版本。
10 是成本因素;使用了 210 次密钥派生函数迭代(顺便说一句,这还不够。我建议使用 12 次或更多的成本。)
vI8aWBnW3fID.ZQ4/zo1G.q1lRps.9cGLcZEiGDMVr5yUP1KUOYTa 是盐和密文,在修改后的 Base-64 中连接和编码。前 22 个字符解码为 salt 的 16 字节值。其余字符是要进行比较以进行身份验证的密文。
此示例取自 documentation for Coda Hale's ruby implementation.
我认为这句话应该措辞如下:
bcrypt 在生成的哈希中内置了盐,以防止彩虹表攻击。
bcrypt
实用程序本身似乎没有维护盐列表。相反,盐是随机生成的,并附加到函数的输出中,以便稍后记住它们(根据 the Java implementation of bcrypt
)。换句话说,bcrypt
生成的“散列”并不是只是的散列。相反,它是散列 和 连接的盐。
Bcrypt
添加“akd2!*”的随机盐,产生“fooakd2!*”,它被散列并存储。后来,我尝试使用密码“bar”登录。要查看我是否正确,它需要散列“barakd2!*”。如果盐是随机生成的,它如何知道如何在散列和比较之前将其添加回“bar”?
bcrypt
知道如何从生成的输出(存储在数据库中)中提取盐。在进行身份验证时,bcrypt
将原始输出分成其哈希和盐分量。 salt 组件应用于用户输入的传入密码。
为了让事情更清楚,
注册/登录方向 ->
密码 + salt 使用从成本、salt 和密码生成的密钥进行加密。我们将该加密值称为 cipher text
。然后我们将盐附加到这个值并使用 base64 对其进行编码。附加成本,这是从 bcrypt
生成的字符串:
$2a$COST$BASE64
该值最终被存储。
攻击者需要做什么才能找到密码? (其他方向 <- )
如果攻击者控制了数据库,攻击者将轻松解码 base64 值,然后他将能够看到盐。盐不是秘密。虽然它是随机的。然后他需要解密 cipher text
。
更重要的是:在这个过程中没有散列,而是 CPU 昂贵的加密 - 解密。因此彩虹表在这里不太相关。
这是一个简单的术语...
Bcrypt没有存储盐的数据库......
盐以 base64 格式添加到哈希中......
问题是 bcrypt 在没有数据库时如何验证密码...?
bcrypt 所做的是它从密码哈希中提取盐......使用提取的盐来加密纯密码并将新哈希与旧哈希进行比较,看看它们是否相同......
让我们想象一个有 1 个散列密码的表。如果黑客获得访问权,他会知道盐值,但他必须为所有常见密码计算一个大列表,并在每次计算后进行比较。这需要时间,而且他只会破解 1 个密码。
想象在同一个表中的第二个散列密码。盐是可见的,但同样的上述计算也需要再次发生才能破解这个,因为盐是不同的。
如果不使用随机盐,它会容易得多,为什么?如果我们使用简单散列,我们可以只生成普通密码的散列 1 一次(彩虹表),然后进行简单的表搜索,或者在 db 表散列和我们预先计算的散列之间进行简单的文件搜索,以找到普通密码。
不定期副业成功案例分享