【问题标题】:How can bcrypt have built-in salts?bcrypt 怎么能有内置的盐?
【发布时间】:2011-10-13 12:44:37
【问题描述】:

Coda Hale 的文章 "How To Safely Store a Password" 声称:

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

他引用了this paper,这说明在OpenBSD 的bcrypt 实现中:

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

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

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

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

简而言之,bcrypt 怎么能有内置的 salts

【问题讨论】:

    标签: security hash internals bcrypt


    【解决方案1】:

    我认为这句话应该是这样写的:

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

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

    【讨论】:

    • 好的,所以我注册了一个站点并选择了密码“foo”。 Bcrypt 添加随机盐“akd2!*”,产生“fooakd2!*”,它被散列并存储。后来,我尝试使用密码“bar”登录。要查看我是否正确,它需要散列“barakd2!*”。如果盐是随机生成的,它如何知道如何在散列和比较之前将其添加回“bar”?
    • @Nathan: bcrypt 知道如何从生成的输出(存储在数据库中)中提取盐。当需要进行身份验证时,bcrypt 将原始输出分为哈希和盐成分。 salt组件应用于用户输入的传入密码。
    • 回答 Nathan Long 的评论,一个好的想法是盐并不意味着保密。这就是为什么盐作为上述答案之一包含在 bcrypt 函数的输出中的原因。盐是为了防止彩虹表,这是常见密码的列表,或者只是蛮力等......不同密码但经过哈希处理。如果没有 salt,数据库 A 中密码的哈希值将与数据库 B 中密码的哈希值相同。Salt 只会更改哈希值,使窃取数据库的人更难解密(取消哈希)密码。跨度>
    • @Nathan 但攻击者能否只删除所有密码中的已知盐,然后用它们创建一个表?
    • 是什么阻止了黑客只是从哈希中删除盐,然后将他的未加盐的哈希与它进行比较?
    【解决方案2】:

    这是 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.

    【讨论】:

    • 您能否详细了解为什么成本系数 10 还不够?在 Grails 中,我注意到 10 是 bcrypt 成本因子/日志轮数的默认值,因此根据您的建议可能值得更新。
    • bcrypt 的成本因数是指数的,或者更确切地说,成本因数 10 意味着 2^10 轮 (1024),成本因数 16 意味着 2^16 轮 (65536)。很自然,这需要 5-10 秒。它应该是成本因子 10 的 64 倍。为了清除其他错误信息,PHP 的 crypt 函数使用了 c 中实现的 unix crypt 库。
    • @TJChambers 没错;如果您可以在帐户上设置密码,您将能够进行身份验证。密码散列并不是为了防止这种攻击。它旨在防止对密码表具有只读访问权限的攻击者进行身份验证。例如,您得到一个带有桌子的备份磁带。
    • 用切片机存储盐不安全吗?如果有人掌握了哈希,只要有足够的计算量,它就可以被破解。如果他不知道盐,那几乎是不可能的。
    • @LobsterMan 不,不是。如果你能保守秘密,你就不会使用这种方法,你只需要存储密码。密码身份验证方案基于攻击者已发现您所知道的一切的假设。盐在那里要求每个密码都被单独攻击。测试密码所需的计算工作由迭代控制。如果用户选择了好的密码,他们将是安全的,即使当盐被泄露时也是如此。在某些情况下,隐藏盐可以帮助密码错误的人,但我会首先处理密码质量。
    【解决方案3】:

    为了让事情更清楚,

    注册/登录方向->

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

    $2a$COST$BASE64

    这个值最终会被存储。

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

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

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

    【讨论】:

      【解决方案4】:

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

      Bcrypt 没有存储盐的数据库...

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

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

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

      【讨论】:

        【解决方案5】:

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

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

        如果不使用随机盐,它会容易得多,为什么?如果我们使用简单的散列,我们可以只为普通密码 1 生成散列(彩虹表),然后进行简单的表搜索,或在 db 表散列和我们预先计算的散列之间进行简单的文件搜索,以找到普通密码。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-01-31
          • 1970-01-01
          • 2018-07-08
          • 1970-01-01
          • 2021-11-30
          • 2018-03-23
          • 2012-10-13
          相关资源
          最近更新 更多