【问题标题】:hash(hash()) vs salted hash哈希(哈希())与加盐哈希
【发布时间】:2011-05-27 20:35:24
【问题描述】:

自从引入Rainbow tables,并且只使用散列密码(例如:MD5)存储在数据库中的密码是not the best secured way

当人们谈论加盐哈希时,总是以这种方式使用它hash(password . salt) 甚至hash(hash(password) . salt)

我不知道为什么要使用salt,并为每个密码添加额外的条目来存储salt? 为什么不直接使用hash(hash(password)),甚至hash(hash(hash(password)))

放盐更安全吗?还是只是更复杂的感觉?

【问题讨论】:

  • 为什么你认为散列散列会增加密码的安全性而不加盐?这就像在不添加秘方中的任何额外调味品的情况下再搅拌面糊 10 分钟一样。

标签: security hash salt password-hash


【解决方案1】:

加盐的目的是让字典攻击变得毫无意义。现在,无论您对哈希进行多少重新哈希,相同的输入总是会产生相同的输出哈希,因此可以为此构建一个字典。因此,虽然多重哈希可能会使蛮力攻击更加困难,但它对字典攻击没有任何作用。

【讨论】:

  • 哈希的意义在于防止预计算攻击。字典攻击仍然是可能的。
  • @Jacco:“hash 的重点是防止预计算攻击”——再来一次?
  • 嗯,没有重读我的评论。加盐哈希的目的是防止预计算攻击。
  • @Jacco,我同意这一点。但这意味着它确实有助于抵御基于字典的攻击,假设字典意味着一组预先计算的值(也许我的字典定义是错误的)。
  • 字典攻击利用了许多密码基于实际单词(如字典中收集的)这一事实。从字典中找到的字符序列创建的 8 个字符串的熵比随机字符串低很多。盐,根据定义是非秘密的,不会改变散列字符串的熵。然而,它确实(试图)确保每个散列都是不同的。因此,尝试字典攻击的攻击者需要单独攻击每个散列值。也就是说,攻击的计算结果不能被重用。
【解决方案2】:

没有什么能阻止任何人为双重哈希密码构建 Rainbow 表。

【讨论】:

    【解决方案3】:

    盐是特定于站点或用户的值。 这意味着,为了检索密码,攻击者必须既可以访问数据库又知道 salt。

    此外,攻击者还可以另外生成一个表,然后将其用于多个站点。但是,使用 salts,攻击者必须为每个站点甚至每个用户生成一个表(使攻击速度变慢)。

    特定于站点的盐对网站的安全性几乎没有增加。正如在 cmets 中所说,结合使用特定于站点的盐和特定于用户的盐可以显着提高安全性,而不是仅使用特定于站点的盐。

    几年前,我在 * 上问了一个关于密码存储的问题,这可能对您有所帮助。见Secure hash and salt for PHP passwords

    【讨论】:

    • 盐通常会在数据库中。毕竟,您需要为每个用户使用不同的盐 - 否则使用相同密码的两个用户将具有相同的哈希值,这是一个安全漏洞。拥有一个特定于站点的密钥意味着如果该单个密钥被泄露,所有您的用户都容易受到一次字典重新计算的攻击。
    • @Jon 是正确的:盐不是“特定于站点的”。它应该为每个用户随机生成。请注意,盐不需要是秘密,也不应被视为秘密。
    • 当然,您可以使用多种盐,例如网站一种每个用户一种...
    • @Jon Skeet - 为什么不两者兼而有之?部分特定于站点且不在数据库中,另一部分特定于用户且在数据库中?编辑:@Lucero Ops。没有看到你的评论...
    • @Lucero:没意义。只需为每个用户提供一个随机的盐。这意味着更少的代码和(可能)更少的错误。不要声称拥有更多的盐会使攻击者更难:事实并非如此。这是通过默默无闻的安全,我们都知道根本不是安全。
    【解决方案4】:

    为简单起见,让我们假设每个人都使用数字作为密码。

    如果每个人都使用 8 位数字作为密码,那就有 100,000,000 种可能性。如果您试图破坏系统,则需要对所有这些可能性进行哈希处理。如果你有一个“hash of hash of hash”,你仍然只需要对这 100,000,000 种可能性进行散列 - 只是以稍微复杂一点的方式。

    现在让我们假设我们也有一个 4 位数的盐。现在,不是 100,000,000 种可能性,而是 1,000,000,000,000 种……我们已经给潜在攻击者 10,000 倍的工作量,而不是 3 倍的工作量。

    基本上,将盐视为一种人为地使每个人的密码变长的方法,从而扩大了字典攻击的空间。

    编辑:为了清楚起见,鉴于盐也是以纯文本形式提供的,您仍然只有 100,000,000 种可能性来尝试攻击任何一个哈希。然而,这意味着在尝试了一个密码的这些可能性之后,攻击者将没有任何有用的信息来攻击另一个密码。没有盐,攻击者可以创建一个包含 100,000,000 种可能性的字典,然后知道数据库中的所有密码,仅给出它们的哈希值。换句话说,盐有助于防止 bulk 攻击。它们还意味着您不能预先生成字典:为了有效地攻击单个密码,您必须事先知道盐。如果没有盐,您可以在访问哈希值之前计算每个可能密码的哈希值。

    【讨论】:

    • 我没有投反对票,但我认为您的解释有点误导。因为盐是已知的,它不会使猜测个人密码变得更加困难。它只是让构建一个表来攻击多个密码变得更加困难,而且只有在每个用户都有不可预测的盐的情况下。
    【解决方案5】:

    您可以基于 hash(hash(pwd)) 的字典构建彩虹表,只需两倍于 hash(pwd) 的时间(甚至更少,因为性能主要与磁盘写入有关),甚至不会较大。使用 salt 极大地扩展了 table 所需的大小,直到它变得不切实际。

    此外(更重要的是),用户通常拥有相同的密码。如果每个用户没有单独的 salt,如果您破坏了一个用户的密码,那么您就破坏了具有相同密码的所有其他用户。

    【讨论】:

    • 但是您如何知道要散列的确切迭代次数?这不也算加盐吗?关于您的第二段,如果您使用随机数而不是静态盐,这只是一个解决方案。
    • Hrm..第二段我从来没有想过。这对于每个用户的盐来说是一个很好的论据。
    • @Alix Axel:当然是对的。我只是暗示每个用户的盐,因为问答似乎很受欢迎,我会相应地修改答案。
    【解决方案6】:

    如果您不使用盐,那么攻击者可以构建一个彩虹表来攻击您数据库中的每个密码。多次哈希并不能在没有盐的情况下保护您,因为彩虹表的工作原理是按照您描述的方式将哈希链接在一起:hash(hash(password))

    如果您为每个用户添加随机盐,那么攻击者将无法重复使用同一张表来破解两个密码,因此他们的工作变得更加困难。另外一个好处是,如果使用盐,两个具有相同密码的用户将散列到不同的值。

    您迭代哈希的想法仍然很好,但您也需要盐。如果你这样做:

    function hashPassword(password, salt) {
        result = hash(salt . password)
        for (i = 0; i < 1000; i++) {
            result = hash(salt . result)
        }
        return result
    }
    

    然后您将攻击者的工作难度提高 1000 倍,而对合法用户的影响可以忽略不计。请注意,攻击者可以在一台低端计算机上每秒测试数百万个候选密码——哈希函数的设计速度很快。这个 1000 次迭代循环可以将一种可行的攻击转变为需要 100 年或更长时间的攻击。当计算机在 18 个月内加速时,只需将迭代次数更改为 2000 次。

    盐、散列算法和迭代计数不需要保密,可以与计算的散列一起存储在您的数据库中。您可以选择固定的迭代次数和哈希算法,但盐必须为每个用户随机生成。

    【讨论】:

    • 投反对票的人愿意发表评论吗?你有什么不同意的吗?
    【解决方案7】:

    迭代哈希和使用盐都可以提高密码哈希的安全性。但它们可以抵御完全不同的攻击。

    迭代哈希会增加暴力攻击所需的工作量。但是你不应该像你建议的那样使用幼稚的迭代,而是为它设计的算法,例如PBKDF2

    盐可以防止预先计算的表格,因此对于每个网站和用户来说应该是不同的。

    【讨论】:

      【解决方案8】:

      我使用一种类似的方法来为登录的用户散列密码。在会话中生成一个盐(随机值)并发送给客户端。用户输入他们的密码,然后用盐进行哈希处理并发回。这样可以确保每次从服务器发送的值都不同,从而更难使用中间人攻击。

      【讨论】: