【问题标题】:How long should a salt be to make it infeasible to attempt dictionary attacks?盐应该多长时间才能使尝试字典攻击变得不可行?
【发布时间】:2012-03-26 01:23:36
【问题描述】:

我正在设计一个如下工作的身份验证系统:

  1. 用户输入密码
  2. 生成盐。
  3. 使用漩涡对密码进行哈希处理
  4. Whirlpool 哈希密码与纯盐连接
  5. 连接的版本使用 sha1 进行哈希处理并存储在数据库中。
  6. 我通过在应用层对密码进行哈希处理来检查密码是否正确,然后执行此操作(在 MySQL 中):

MySQL

WHERE `Password` = SHA1(CONCAT('$hashedPassword',`Salt`)) AND [..]

目前我的盐是 64 字节。这足以使字典攻击不可行吗?

我确定 sha1 存在已知漏洞,但它是我的 MySQL (5.1) 版本中唯一可用的函数,我可以在数据库层使用,而不是在应用程序和数据库之间的连接上选择普通 salt层。

【问题讨论】:

  • 64 字节听起来方式足够长。但我一定是错过了什么。以后如何验证密码?您需要使用与保存时相同的盐重复哈希序列,但您不会将盐存储在任何地方,只有盐的 SHA-1 和其他东西。
  • 应用层只关心whirlpool对明文密码进行散列。它以SHA1(CONCAT(PHP_WHIRLPOOL('correct horse battery staple'), Salt)) 的形式存储在 MySQL 中,其中 PHP_WHIRLPOOL 发生在应用程序上。希望这是有道理的? :)
  • 是的,该伪代码与我认为您在文本中的意思相符。所以我还是不明白你是如何重复操作进行验证的。我想也许您的意思是说 SHA-1 结果 盐都存储在 MySQL 中?
  • 是的,哈希密码和盐的sha1结果存储在密码字段中。
  • @Will Morgan: Soo,那你要如何验证密码呢?您需要将盐存储在单独的数据字段中。另外,盐的长度并没有太大的影响,只要它足够长以至于已经存在彩虹表的概率非常低(这里8字节应该绰绰有余)。

标签: security encryption cryptography passwords salt


【解决方案1】:

8 字节就足够了。

当您在 /etc/shadow 中查看 Linux(内核版本 3.16)中保存用户密码的位置时,您可以看到如下形式:

$id$salt$encrypted_pa​​ssword

  • id 定义哈希算法

在我的 Linux 版本中,salt 是 8 位数字,因此是 8 个字节,我认为内核开发人员知道他们在做什么,所以我也使用 8 字节作为我的 salt,它来自加密安全伪随机生成器源。在 Linux 中,您可以简单地读取 /dev/random(熵低时阻塞)或 /dev/urandom(不阻塞)。

有关更多信息,请阅读 crypt 的手册页。

【讨论】:

    【解决方案2】:

    书:

    密码学工程:设计原理与实际应用

    Niels Ferguson、Bruce Schneier 和 Tadayoshi Kohno

    21.2.1 腌制和拉伸

    由于位很便宜,为简单起见,我们建议使用 256 位盐。

    【讨论】:

      【解决方案3】:

      我认为您误解了盐的概念。 盐不会显着阻止或减缓字典和蛮力攻击

      使用盐的全部意义在于避免有人已经为您的密码哈希预计算字典/暴力攻击(例如使用彩虹表)。因此,它只需要足够长以排除特定盐的此类表已经存在的可能性。

      考虑到这种彩虹表的典型大小,极不可能有人已经预先计算了这样的表,用于甚至小到 8 字节左右的盐(考虑可能的盐的数量:256^8 = 18446744073709551616)。前提当然是盐是随机生成的,并且您不会多次使用相同的盐值。 64字节不会有什么坏处,当然,这没有什么问题。

      但是,如果您想让暴力破解或字典攻击变得不可行,那么使用更长的盐对您没有帮助。相反,让您的用户选择强密码并考虑使用key stretching

      【讨论】:

      • Salt 不会显着减慢针对单个用户的攻击,但会减慢针对大型数据库的攻击速度。考虑拥有一个无盐数据库,现在您从常用密码开始,计算一次哈希,然后对照整个数据库进行检查。然后进入下一个常用密码。这不适用于盐。
      【解决方案4】:

      盐决定了存储预先计算的表(例如彩虹表)需要多少空间,使攻击者可以快速查找给定哈希的密码。

      哈希迭代次数(不是盐)决定了攻击者尝试其候选字典中的每个密码所需的时间

      每一点盐都会使查找表所需的空间增加一倍。因此,8 个字节(64 位)将产生 1600 万 TB 的空间乘数 — 使总空间达到 yottabyte 范围(并且可能超出大多数攻击者的范围)。

      【讨论】:

      • 彩虹表不会凭空出现。它们需要计算。使用更长的盐还显着增加了为长达固定长度的密码创建彩虹表所需的时间(直到首先实际生成这些表变得不可行的程度)。即使一个人有足够的空间来存储假设的 Yottabyte 表,他也几乎没有足够的计算能力甚至存储带宽来首先生成它。
      • 如何生成彩虹表来攻击加盐密码?
      • 要么为每个可能的盐值创建单独的表,要么(在假设的 8 字节散列的情况下)找到将散列值转换为盐和密码的笛卡尔积的归约函数空间。不过,后者有点不必要,因为攻击者应该知道盐。然而,这不是我的意思,我只是想补充一点,盐的熵不仅成倍地增加了存储查找表所需的 空间,它还增加了 时间 i> 以指数方式生成这些表,这至少同样重要。
      • 这一切都没有实际意义,因为盐使预计算变得不可行。没有人能负担得起计算表格或存储表格的能量。
      【解决方案5】:

      我的Practical Cryptography (Ferguson, Schneier) 的版权日期为 2003 年,建议使用 256 位(32 字节)作为盐长度。它说 128 位“可能”是可以的,但是,正如它所指出的那样,位很便宜。鉴于此,为每个密码在磁盘上存储 64 字节盐的相对最低成本似乎是合理的。这可能是矫枉过正,但不会受到伤害。

      您可能还需要考虑password stretching(多次重复哈希函数)来增加通过暴力破解密码的计算复杂性。将检查密码的成本增加几百毫秒会大大增加暴力攻击的成本。

      【讨论】:

      • 关于“键拉伸”的维基百科文章是错误的。它描述了关键加强。密钥拉伸是一种允许您导出比底层哈希函数的输出大小更长的输出的方法,并且是将 PKCS#5 PBKDF2 与 PBKDF1 分开的原因之一。
      • 嗯,这并不能说明它是正确的,而且这种用法必然会在不熟悉密码学的读者中引起混淆。关键强化是您在指定高迭代次数时所做的事情。当您需要特定长度的派生密钥时,您会执行密钥拉伸。如果您使用 PBKDF 为 AES-256-CBC 加密生成 256 位密钥,则您执行了密钥拉伸,但不一定执行密钥强化。您不一定会因为派生密钥是 256 位而获得 256 位的安全性。
      • @HenrickHellström:我同意正确使用这些条款很重要。您是否有这些术语的最终参考资料?我现在注意到这个问题的公认答案使用相同的术语并引用相同的 wiki 页面(或指向相同的页面)。
      • 我认为(ab)使用术语“键拉伸”来加强键可以追溯到 2005 年 Frances F. Yao 和 Yiqun Lisa Yin 的一篇文章,他们在其中讨论了将迭代作为一种方法“拉伸”密码的熵。可以找到使用“键拉伸”作为键扩展的同义词,例如这里rfc-editor.org/rfc/pdfrfc/rfc5931.txt.pdf
      • 另一个使用“键拉伸”作为键扩展同义词的示例:tools.ietf.org/html/draft-krovetz-umac-01。还有一个:gnu.org/software/gnu-crypto/manual/api/gnu/crypto/prng/…
      【解决方案6】:

      盐用于向make certain attacks less efficient 的密码添加额外的随机位。所以盐增加的熵越多越好。

      目前,PKCS #5 建议使用至少 64 位熵的盐长度,通常建议使用 bcrypt uses 128 bits,您甚至可以使用更多。但肯定有一点你不会增加额外的实际复杂性,因为由此产生的复杂性已经是空想的。

      因此,每个密码应该至少有一个唯一的盐,以便一次只能破解一个密码。充其量,使用已经过验证的密码存储方案。

      【讨论】:

        猜你喜欢
        • 2012-07-04
        • 1970-01-01
        • 2011-11-02
        • 2015-01-22
        • 2022-01-05
        • 2010-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多