【问题标题】:Non-random salt for password hashes密码哈希的非随机盐
【发布时间】:2010-10-06 21:36:26
【问题描述】:

更新:我最近从this question 了解到,在下面的整个讨论中,我(我相信其他人也是如此)有点令人困惑:我一直称之为彩虹表的东西实际上被称为哈希表。彩虹表是更复杂的生物,实际上是 Hellman Hash Chains 的一种变体。虽然我相信答案仍然是一样的(因为它不归结为密码分析),但有些讨论可能有点歪曲。
问题:“What are rainbow tables and how are they used?


通常,我总是建议使用加密强度高的随机值作为盐,与散列函数(例如密码)一起使用,以防止彩虹表攻击。

但是,salt 的随机性实际上在密码学上是必要的吗?在这方面是否有任何唯一值(每个用户唯一,例如 userId)就足够了?实际上,它会阻止使用单个 Rainbow Table 来破解系统中的所有(或大部分)密码...
但是缺乏熵真的会削弱散列函数的加密强度吗?


注意,我不是在问为什么要使用盐,如何保护它(它不需要),使用单个常量哈希(不要),或者使用什么样的哈希函数。 只是盐是否需要熵。


到目前为止,感谢大家的回答,但我想专注于我(有点)不太熟悉的领域。主要是对密码分析的影响 - 如果有人对密码数学 PoV 有一些意见,我将不胜感激。
此外,如果还有其他未考虑的向量,这也是很好的输入(请参阅@Dave Sherohman 关于多个系统的观点)。
除此之外,如果您有任何理论、想法或最佳实践 - 请用证据、攻击场景或经验证据支持这一点。甚至是对可接受的权衡的有效考虑...我熟悉该主题的最佳实践(资本 B 资本 P),我想证明这实际上提供了什么价值。


编辑:这里有一些非常好的答案,但我认为正如@Dave 所说,它归结为常见用户名的彩虹表......以及可能不太常见的名称。但是,如果我的用户名是全球唯一的怎么办?对于我的系统来说不一定是唯一的,但对于每个用户来说都是独一无二的——例如电子邮件地址。
没有动力为单个用户构建 RT(正如@Dave 强调的那样,盐不是保密的),这仍然会阻止集群。唯一的问题是我可能在不同的站点上拥有相同的电子邮件和密码 - 但无论如何 salt 不会阻止这种情况。
所以,它回到密码分析——熵是否必要? (我目前的想法是从密码分析的角度来看没有必要,但出于其他实际原因。)

【问题讨论】:

  • 我必须说我对下面的许多答案感到困惑。使用盐的主要目的只是为了防止彩虹表攻击,因此用户应该做任何独特的事情,因为它迫使攻击者为每个盐重新创建一个彩虹表。我的 2c。
  • 如果需要盐,例如。网站,您通常在 url 中有 id。如果攻击者知道(我们假设他知道)密码 salt 是用户 ID+用户名,那么修改攻击以避免 salt 值是相当容易的。
  • @dmajkic,salt 旨在防止 RT 攻击并区分不同用户的相同密码。这是给定的,即使有用户名。
  • @AviD:确实如此。但是,如果我知道我的 pass 的哈希值,并且我知道 salt 是我的用户名,那么我可以轻松地为任何字典单词创建 hashed pass 值,并将其与其他人 pass hash 进行比较。这就是为什么时间比用户名更好。
  • 刚刚在 PHP 安全联盟网站上发现一篇文章,在其示例中使用 md5 散列 随机 数字作为盐 phpsec.org/articles/2005/password-hashing.html

标签: security authentication hash cryptography password-protection


【解决方案1】:

确实,单独的用户名可能会出现问题,因为人们可能会在不同的网站之间共享用户名。但是,如果用户在每个网站上使用不同的名称,这应该是没有问题的。那么为什么不让它在每个网站上都是独一无二的呢。哈希密码有点像这样

hashfunction("www.yourpage.com/"+username+"/"+password)

这应该可以解决问题。我不是密码分析大师,但我肯定怀疑我们不使用高熵这一事实会使哈希变得更弱。

【讨论】:

  • 我相信你是对的,这就足够了。但是,鉴于哈希是一个复杂的数学领域,并且很有可能低熵盐使哈希在未来更容易被猜测(尤其是当哈希被破坏时),我不会在安全性上打赌,当证明更安全的解决方案并不昂贵。
【解决方案2】:

使用高熵盐对于安全存储密码是绝对必要的。

使用我的用户名“gs”并将其添加到我的密码“MyPassword”中会得到 gsMyPassword。使用彩虹表很容易打破这一点,因为如果用户名没有足够的熵,则可能是该值已经存储在彩虹表中,尤其是在用户名很短的情况下。

另一个问题是您知道用户参与了两个或多个服务的攻击。有很多常见的用户名,可能最重要的用户名是 admin 和 root。如果有人创建了一个彩虹表,其中包含最常见用户名的盐,他可以使用它们来破坏帐户。

They used to have a 12-bit salt。 12 位是 4096 种不同的组合。这还不够安全,因为that much information can be easily stored nowadays。这同样适用于 4096 个最常用的用户名。您的一些用户可能会选择属于最常用用户名的用户名。

我找到了这个password checker,它可以计算出密码的熵。密码中的熵较小(例如通过使用用户名)使得彩虹表更容易,因为它们尝试覆盖至少所有低熵密码,因为它们更有可能发生。

【讨论】:

  • +1 很好,但是,您正在谈论一个潜在的巨大彩虹表。要覆盖 gsMyPassword 长度的所有值(假设混合大小写字母数字),表中需要 36^12 行!
  • 作为后续:分布式彩虹表项目有 63,970 个破解哈希,但 36^12 是 4,738,381,338,321,616,896!
  • 谢谢,这确实有道理 - 但正如 Mr.PotatoHead 指出的那样,您仍在扩展您的空间,超出了使用 RT 破解的合理可能性。另外,假设我的用户名至少有 6-8 个字符 - 熵提供了哪些额外的必要值?
  • @Potato:您假设一个彩虹表包含所有可能的字母数字字符组合。不必如此,有效的彩虹表将包含常见密码/哈希的哈希。盐可以防止字典攻击或组合彩虹表/字典。
  • 使用用户名作为 salt 对于存在可预测命名的高权限帐户的系统也是一个坏主意:Windows 上的“Administrator”,*nix 上的“root”,*nix 上的“sa” MSSQL等
【解决方案3】:

Salt 传统上存储为哈希密码的前缀。这已经让任何有权访问密码哈希的攻击者都知道了。是否使用用户名作为 salt 不会影响该知识,因此不会影响单系统安全性。

但是,使用用户名或任何其他用户控制的值作为 salt 会降低跨系统安全性,因为在使用相同密码哈希算法的多个系统上拥有相同用户名和密码的用户最终会得到相同的结果每个系统上的密码哈希。我不认为这是一项重大责任,因为作为攻击者,我会先尝试已知目标帐户已在其他系统上使用过的密码,然后再尝试任何其他方式来破坏该帐户。相同的哈希值只会提前告诉我已知密码会起作用,它们不会使实际攻击变得更容易。 (但请注意,帐户数据库的快速比较将提供更高优先级目标的列表,因为它会告诉我谁在重复使用密码,谁没有重复使用密码。)

这种想法的更大危险是用户名通常会被重复使用——例如,几乎任何你想访问的网站都会有一个名为“Dave”的用户帐户,而“admin”或“root”则更为常见——这将使构建针对具有这些常用名称的用户的彩虹表变得更加容易和有效。

在散列密码之前,可以通过在密码中添加第二个盐值(固定和隐藏或像标准盐一样暴露)来有效解决这两个缺陷,但是,此时,您还不如只使用标准熵无论如何都要加盐而不是将用户名放入其中。

编辑添加:很多人都在谈论熵以及盐中的熵是否重要。是的,但不是因为它上面的大多数 cmets 似乎认为的原因。

一般的想法似乎是熵很重要,因此攻击者很难猜到盐。这是不正确的,事实上,完全不相关。正如许多人多次指出的那样,受盐影响的攻击只能由拥有密码数据库的人进行,拥有密码数据库的人可以查看每个帐户的盐是什么。当您可以轻松查找它时,它是否可以猜测并不重要。

熵之所以重要是为了避免盐值的聚类。如果盐是基于用户名的,并且您知道大多数系统将有一个名为“root”或“admin”的帐户,那么您可以为这两种盐制作一个彩虹表,它将破解大多数系统。另一方面,如果使用随机的 16 位盐并且随机值大致均匀分布,那么您需要一个彩虹表来存储所有 2^16 种可能的盐。

这不是要阻止攻击者知道个人帐户的盐是什么,而是要不向他们提供单一盐的大而肥的目标,该目标将用于相当大比例的潜在目标。

【讨论】:

  • 同意。我会更加强调用户名的重用,因为我认为这是对使用用户名作为盐的假设系统最有可能成功的攻击,而对使用随机盐的系统则可能会失败。
  • 感谢您关于跨系统安全的观点。另外,我猜将来它不太可能看到用户特定的彩虹表......
  • @AviD:你这么认为?这是一个非常具体的攻击向量。
  • 不用于生成盐,不。唯一受盐影响的攻击是直接针对散列密码的攻击。除非他们拥有您的密码数据库的副本,否则攻击无法进行此类攻击,该副本也将包含盐。由于攻击者已经拥有了盐,他们只需要足够的熵来避免使用相同的盐散列多个密码,任何基本的 (P)RNG 都将提供。
  • @acidzombie24:对所有密码使用单个固定盐(根据我的经验通常称为“随机数”)不如为每个密码使用唯一的随机盐安全,因为它仍然允许攻击者轻松确定两个或多个帐户是否共享相同的密码。另一方面,nonce 可能存储在您的代码中而不是数据库中,因此 拥有您的数据库的攻击者将无权访问它;因此,最安全的选择是同时使用系统范围的随机数(存储在代码中)和每个密码的盐(存储在数据库中)。
【解决方案4】:

哈希函数的强度不是由它的输入决定的!

使用攻击者已知的盐显然会使构建彩虹表(特别是对于像 root 这样的硬编码用户名)更具吸引力,但不会削弱 散列。使用攻击者未知的盐会使系统更难攻击。

用户名和密码的连接可能仍然为智能彩虹表提供一个条目,因此使用一系列伪随机字符的盐,与散列密码一起存储可能是一个更好的主意。例如,如果我有用户名“potato”和密码“beer”,则您的哈希的串联输入是“potatobeer”,这是彩虹表的合理条目。

每次用户更改密码时更改盐值可能有助于抵御长期攻击,实施合理的密码策略(例如混合大小写、标点符号、最小长度、n 周后更改。

但是,我想说您选择的摘要算法更为重要。例如,对于生成彩虹表的人来说,使用 SHA-512 比使用 MD5 更痛苦。

【讨论】:

  • 函数的强度没有改变,但输出肯定会改变。如果输入可以被影响或知道,那么也许可以推断出一些关于哈希值的东西。这就是风险。
  • @Martin:你应该能够从哈希值中推断出你是否有匹配项!将“roota”或“rootb”(其中“a”和“b”代表密码)放入散列函数会得到完全不同的输出。
  • 使用彩虹表的攻击者知道盐。
  • 服务器必须知道未加密的盐(根据哈希检查密码),因此可以想当然地认为攻击者可以访问盐,或者可以很容易地得到它。跨度>
  • 对,对,这是给定的——更具体地说,我指的是整个密码系统(或子系统,wrt 哈希)的强度。
【解决方案5】:

我会说,只要每个密码的盐值不同,你可能就可以了。加盐的要点是,您不能使用标准的彩虹表来解决数据库中的每个密码。因此,如果您对每个密码应用不同的盐(即使它不是随机的),攻击者基本上必须为每个密码计算一个新的彩虹表,因为每个密码使用不同的盐。

使用熵更多的盐并没有太大帮助,因为在这种情况下,假定攻击者已经拥有数据库。由于您需要能够重新创建哈希,因此您必须已经知道盐是什么。因此,无论如何,您都必须将盐或构成盐的值存储在文件中。在像 Linux 这样的系统中,获取盐的方法是已知的,因此拥有秘密盐是没有用的。您必须假设拥有您的哈希值的攻击者也可能知道您的盐值。

【讨论】:

  • "加盐的关键在于,您不能使用标准的彩虹表来解决数据库中的每个密码"。我不同意。关键是您不能使用标准的彩虹表来解决 any 密码。如果盐是用户名,那么“root”的彩虹表可以破解root的密码。
  • 这可能是唯一真正重要的规则。基本上,您希望密码在您的系统上是唯一的,但这并不重要。不过,它仍然可能很简单。你不需要大量的熵。
  • 这也是我的想法 - 但我被困在“可能还好”。我仍然没有看到这个理论被证明——或者至少证明没有密码分析的含义。
  • @onebyone:如果 salt 由用户名 + 常量局部值组成(我经常使用 ':'),那么这仍然会破坏标准化的 RT,除非它们中有多种包括常见的用户名和常见的静态盐值。我宁愿怀疑情况并非如此。
  • 与 nsayer 一起。您可以使用系统范围的常量字符串,不会有预生成的 RT,例如 #(D83d8,以及作为盐的用户名,这可能会防止任何彩虹表攻击。
【解决方案6】:

我喜欢同时使用这两种方法:每条记录的高熵随机盐,以及记录本身的唯一 ID。

虽然这并没有增加对字典攻击等的安全性,但它确实消除了一些边缘情况,即有人将他们的盐和哈希复制到另一条记录以用他们自己的密码替换密码。

(诚然,很难想到这种情况适用的情况,但在安全方面,我认为安全带和背带没有任何危害。)

【讨论】:

  • 我喜欢将密码绑定到用户的想法。但如果攻击者可以更改密码,他肯定也可以更改 id。
  • 取决于ID是什么:我用的是表的主键,你真的不能随意改变。 TBH,如果黑客正在写入您的数据库,那么您已经遇到了很大的麻烦......
  • 不,我喜欢它。攻击者通常是“内部人员”。我可以想象他所追求的东西不在数据库中的场景,但如果他可以覆盖数据库中的凭据,他就可以验证自己的身份来做他想做的事。
  • 好点。我们确实发现将第一个用户添加到新数据库变得越来越困难:我们有效地使我们的应用程序非常安全,我们几乎无法自己破解它们。 [另外,使用特定于应用程序的 salt,这样您就无法将凭据从一个数据库复制到另一个数据库。]
  • 最后我发现有人这么说:在盐中添加一些用户特定的东西。这样可以避免入侵者将某些凭据复制给另一个用户,并且还可以防止黑客在设法访问表中的哈希和盐字段时猜出任何密码。我还喜欢做一件事:不在散列中使用整数次迭代。不要选择 10k 或 20k,而是 19835 之类的。
【解决方案7】:

如果盐是已知的或容易猜到的,则说明您没有增加字典攻击的难度。甚至可以创建一个将“恒定”盐考虑在内的修改后的彩虹表。

使用独特的盐会增加 BULK 字典攻击的难度。

拥有独特的、加密性强的盐值将是理想的。

【讨论】:

  • 彩虹表的重点是这些值是预先生成的,如果你为一个值(例如密码)加上一个给定的常数生成表,那是完全不同的表,您不妨直接尝试哈希结果。 :)
  • 一个常量哈希值一文不值。所有密码都可以使用一个彩虹表破解。盐的目的是不可能创建彩虹表。
  • @gs - 我不明白为什么你不能构建一个“包含”恒定盐的影响的彩虹表。攻击空间(密码)不会改变,因此表不需要增长,只需​​重新计算即可。
  • @Potato - 取决于权衡。如果您公司的 20k 登录都使用相同的常量盐进行哈希处理,那么计算一个新的彩虹表可能比单独对它们进行字典攻击更便宜。因此我强调“BULK”。
【解决方案8】:

Salt 应具有尽可能多的熵,以确保如果给定的输入值被多次散列,则生成的散列值将尽可能接近,始终不同。

在盐中使用具有尽可能多熵的不断变化的盐值将确保散列的可能性(例如,密码 + 盐)将产生完全不同的散列值。

盐中的熵越少,生成相同盐值的机会就越大,因此生成相同哈希值的机会就越大。

当输入已知且“恒定”时,哈希值的性质是“恒定”的,这使得字典攻击或彩虹表如此有效。通过尽可能多地改变生成的哈希值(通过使用高熵盐值)确保对相同的输入+随机盐进行哈希处理会产生许多不同的哈希值结果,从而击败(或至少大大降低)彩虹表的有效性攻击。

【讨论】:

  • 再一次,我不是指使用单一的常量盐——而是一个非随机的,但用户唯一的值。例如。用户 ID。
  • 重点是你的盐值永远不应该是恒定的。您散列的每一件事,无论是否相同的“输入”值都应该有不同的盐。 Dave Sherohman 解释了使用用户唯一值的缺点,这些值在每次哈希计算中基本保持不变。
【解决方案9】:

熵是盐值的点。

如果盐背后有一些简单且可重复的“数学”,那么它与盐不存在相同。只需添加时间价值就可以了。

【讨论】:

  • 我不明白这条评论。你说“使用熵”,然后:“使用时间”??
  • 如果用户名用于盐,它的熵不够。但是如果你使用用户名+当前日期时间 - 它是,因为很难猜出确切的盐是什么时候创建的。
  • “足够熵”是主观的;这是你的标准。基于时间的这个值可能不够。
  • 没有“绝对真实随机性”这样的东西。当它“足够好”时,我们要说。如果您认为在这种情况下时间不够好,请使用其他东西。 *.com/questions/84556/…
  • 其实salt的意义在于让每个hash结果都是唯一的——为了防止(a)彩虹表攻击,以及(b)相同的密码识别。问题是,熵还需要什么?
最近更新 更多