非随机盐用于密码哈希

93

更新: 我最近从这个问题中了解到,在下面的讨论中,我(我确定其他人也是)有点混淆:我一直称为彩虹表的东西实际上被称为哈希表。 彩虹表是更复杂的生物,实际上是Hellman Hash Chains的变体。 虽然我认为答案仍然相同(因为它并不涉及密码分析),但一些讨论可能有些偏差。
问题是:“什么是彩虹表,它们如何使用?


通常,我总是建议使用具有加密强度的随机值作为盐,与哈希函数一起使用(例如用于密码),以防范彩虹表攻击。

但实际上,盐是否必须是随机的呢?在这方面,任何唯一值(每个用户唯一,例如userId)都可以吗?实际上,这将防止使用单个彩虹表破解系统中的所有(或大多数)密码...
但缺乏熵是否真的会削弱哈希函数的加密强度?


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


感谢所有的回答,但我想集中讨论我(有点)不熟悉的领域。主要是对密码分析的影响 - 如果任何人从密码数学的角度提供一些信息,我将不胜感激。
此外,如果还有其他未被考虑的向量,那就是很好的输入(请参见@Dave Sherohman关于多个系统的观点)。
除此之外,如果您有任何理论、想法或最佳实践,请用证据、攻击场景或经验证明支持这一点。或者甚至考虑可接受的权衡的有效考虑... 我熟悉有关此问题的最佳实践(大写B大写P),我想证明这实际上提供了什么价值。


编辑:这里有一些非常好的回答,但我认为正如@Dave所说,它归结为对常见用户名的彩虹表攻击...以及可能是不太常见的用户名。但是,如果我的用户名在全球范围内都是唯一的呢?不一定是针对我的系统而言是唯一的,而是针对每个用户都是唯一的——例如电子邮件地址。
对于一个单独的用户没有建立彩虹表的动机(正如@Dave强调的那样,盐值并不是保密的),这仍然可以防止聚类。唯一的问题是我可能在另一个网站上使用相同的电子邮件和密码——但无论如何盐值也无法防止这种情况。
因此,问题又回到了密码学分析——是否需要熵?(我目前的想法是从密码学分析的角度来看它不是必需的,但出于其他实际原因则是必需的。)


我必须说,对于下面的许多答案,我感到困惑。使用盐的主要目的只是为了防止彩虹表攻击,所以任何与用户相关的唯一信息都可以使用,因为这会迫使攻击者为每个盐重新创建一个彩虹表。我的两分意见。 - Goran
如果需要盐来保护网站,通常在URL中会有ID。如果攻击者知道(我们假设他知道)密码盐是用户ID+用户名,那么修改攻击以避免盐值就相当容易了。 - dmajkic
@dmajkic,salt 旨在防止 RT 攻击并区分不同用户的相同密码。即使使用用户名,这也是必须的。 - AviD
传统上,试图改进已经取得的成果是容易出错的,至少可能会分散注意力。从我看来,唯一的问题是确保盐的长度足够长以启动事情——然后基本上就是算法的强度了,不是吗? - Nicholas Jordan
1
刚在PHP安全联盟网站上发现一篇文章,其中的示例使用了一个md5哈希的“随机”数字作为盐。http://phpsec.org/articles/2005/password-hashing.html - helloworlder
显示剩余2条评论
9个回答

160
传统上,Salt(盐值)被存储为散列密码的前缀。这使得任何可以访问密码哈希值的攻击者已经知道了它。使用用户名作为盐值或不使用都不会影响单个系统的安全性。
然而,使用用户名或任何其他用户可控制的值作为盐值将降低跨系统的安全性,因为在使用相同密码哈希算法的多个系统中具有相同用户名和密码的用户将在每个系统中得到相同的密码哈希。我认为这不是一个重大的问题,因为作为攻击者,我会首先尝试目标账户已知在其他系统上使用过的密码,然后才尝试任何其他入侵方法。相同的哈希只会事先告诉我已知的密码是否有效,但不会使实际攻击更容易。(请注意,快速比较账户数据库将提供更高优先级的目标清单,因为它将告诉我谁正在重用密码以及谁没有重用密码。)
来自这个想法的更大的危险是用户名通常被重复使用 - 任何您想访问的网站几乎都会有一个名为“Dave”的用户账户,例如,“admin”或“root”更加常见 - 这将使得构建针对那些常见名称的用户的彩虹表变得更加容易和有效。
这些缺陷都可以通过在散列密码之前添加第二个盐值(固定和隐藏或像标准盐一样公开)来有效解决,但此时,您可能完全可以使用标准熵盐,而不必将用户名纳入其中。
补充编辑:很多人谈论熵以及盐中的熵是否重要。它确实很重要,但原因并非大多数评论所认为的那样。一般认为熵(entropy)很重要,这样盐值就不容易被攻击者猜到。这是不正确的,事实上,完全无关紧要。正如一些人多次指出的那样,只有拥有密码数据库的人才能使用受盐影响的攻击方式,而拥有密码数据库的人可以查看每个账户的盐值。当您可以轻松地查找时,它是否可猜测并不重要。
熵的重要性在于避免盐值聚类。如果盐基于用户名,并且您知道大多数系统都将有一个名为“root”或“admin”的帐户,则可以为这两种盐制作彩虹表,并破解大多数系统。另一方面,如果使用随机的16位盐,而随机值具有大致均匀的分布,则需要为所有2^16个可能的盐制作彩虹表。
这不是为了防止攻击者知道每个账户的盐是什么,而是不给他们提供单个盐的大目标,该盐将用于大量潜在目标中的一个。

我很欣赏你对跨系统安全的看法。此外,我猜想未来很可能会出现用户特定的彩虹表... - AviD
@AviD:你这么认为吗?那是一个非常具体的攻击向量。 - David Grant
2
不是用于生成盐。受盐影响的攻击仅限于直接针对散列密码的攻击。除非攻击者拥有您的密码数据库副本,否则无法进行此类攻击,该数据库还将包含盐。由于攻击者已经拥有盐,因此他们只需要足够的熵来避免使用相同盐散列多个密码,而任何基本的(P)RNG都可以提供这种熵。 - Dave Sherohman
3
在我的经验中,对于所有密码使用单个固定的盐(通常称为“nonce”)比对每个密码使用唯一随机盐更不安全,因为它仍然允许攻击者轻易确定两个或更多帐户是否共享相同的密码。另一方面,nonce 可能存储在您的代码而不是数据库中,因此只有您的数据库的攻击者将无法访问它;因此,最安全的选择是同时使用系统范围的 nonce(存储在代码中)和每个密码的盐(存储在数据库中)。 - Dave Sherohman
@DaveSherohman 非常有用的信息,基于这个建议,我找到了一个非常好的Java库,可以做你所建议的事情:http://www.jasypt.org/encrypting-passwords.html - JMac
显示剩余9条评论

31

使用高熵盐非常必要以确保密码的安全存储。

以我的用户名“gs”为例,加上我的密码“MyPassword”就得到了gsMyPassword。这个密码很容易被彩虹表攻破,因为如果用户名没有足够的熵值,它可能已经被存储在彩虹表中,特别是如果用户名很短。

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

过去他们使用12位的盐。12位有4096种不同的组合方式。这已经不够安全了,因为现在很容易存储那么多信息。对于最常用的4096个用户名也是如此。你的一些用户很可能会选择属于最常见用户名之一的用户名。

我找到了这个密码检查器,它可以计算出你的密码熵值。使用较小的密码熵值(比如使用用户名)会让彩虹表更容易覆盖所有密码,因为它们更容易出现。


@Potato:你假设彩虹表包含所有可能的字母数字组合。实际上这并不一定是必要的,一个有效的彩虹表将会包含常见密码/哈希值的散列。盐值的作用在于保护免受字典攻击或组合彩虹表/字典攻击。 - Guillaume
@ Mr. Potate Head:它提到了“完成10181000万条链”,这可能是多少个哈希呢? - Georg Schölly
@gs:不太确定 - 我见过一些关于链的东西,但更有趣的是数据库 - 它们可以达到8个混合字母数字字符! - David Grant
3
在系统中使用用户名作为盐(salt)也不是一个好主意,尤其是在存在可预测命名的高特权账户的情况下,例如Windows上的“Administrator”,*nix上的“root”,MSSQL中的“sa”等。 - nobody
“高熵盐”并不是更大的搜索空间,因为“盐部分”总是已知的。假设您有4096位盐和8位密码。只要您知道盐的值,您的搜索空间就只有256。 - Sedat Kapanoglu
显示剩余6条评论

8

确实,仅使用用户名可能会存在问题,因为人们可能在不同的网站之间共享用户名。但是,如果用户在每个网站上有不同的名称,那么这应该不成问题。因此,为什么不在每个网站上都将其设置为唯一值呢?可以使用以下方式对密码进行哈希处理:

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

这样应该就能解决问题了。虽然我不是密码分析大师,但我确信我们不使用高熵值不会使哈希变得更弱。


我相信你说的没错,这已经足够了。然而,考虑到哈希是一个复杂的数学领域,低熵盐可能会使哈希更容易被猜测(特别是在哈希被破解时),我不会把赌注押在安全性上,尤其是当已经证明更安全的解决方案并不昂贵的情况下。 - Georg Schölly

7

我喜欢同时使用高熵的随机单记录盐和记录本身的唯一ID。

虽然这对于字典攻击等方面的安全性没有太大帮助,但它确实消除了这样一种边缘情况:有人复制他们的盐和哈希到另一个记录,并打算用自己的密码替换它。

(诚然,很难想象这种情况的适用性,但在安全方面,多加保护无妨。)


我喜欢将密码绑定到用户的想法。但如果攻击者可以更改密码,那么他肯定也可以更改ID。 - Georg Schölly
取决于ID是什么:我使用表的主键,这是您无法随意更改的。说实话,如果黑客正在向您的数据库写入数据,那么您已经遇到了相当大的麻烦... - teedyay
3
不,我喜欢它。攻击者通常是“内部人员”。我可以想象出一些情况,他所追求的并不在数据库中,但如果他能够覆盖数据库中的凭据,他就可以进行身份验证以执行他想要的操作。 - erickson
1
好的观点。我们发现将第一个用户添加到新数据库变得越来越困难:我们已经使我们的应用程序非常安全,以至于我们自己几乎无法破解它们。[此外,使用特定于应用程序的盐,这样您就无法从一个数据库复制凭据到另一个数据库。] - teedyay
最后我找到有人说过这个:将一些特定于用户的内容添加到盐中。这可以避免入侵者将某些凭据复制到另一个用户,并且还可以防止黑客猜测密码,如果他成功到达表中的哈希和盐字段。我还喜欢做一件事:不要在散列中使用循环次数的圆整数字。不要选择10k或20k,而是选择像19835这样的数字。 - Andrew

3

哈希函数的强度不取决于其输入!

对于硬编码的用户名(如root),使用已知于攻击者的盐显然会使构建彩虹表变得更加有吸引力,但它并不会削弱哈希值。使用攻击者不知道的盐将使系统更难以攻击。

用户名和密码的连接仍可能为智能彩虹表提供入口,因此使用一系列伪随机字符的盐,并将其与散列密码一起存储,可能是一个更好的选择。例如,如果我的用户名是 "potato",密码是 "beer",则您的哈希的连接输入为 "potatobeer",这是彩虹表的一个合理入口。

每次用户更改密码时更改盐可能有助于打败长时间的攻击,实施合理的密码策略也可能有所帮助,例如混合大小写、标点符号、最小长度,在 n 周后更改。

然而,我认为你的哈希算法的选择更重要。例如,使用 SHA-512 比使用 MD5 更能够防止生成彩虹表的人痛苦。


函数的强度不会改变,但输出肯定会改变。如果输入可以被影响或知道,那么可能可以推断出哈希值。这就是风险。 - Martin Carpenter
@Martin:从哈希值中你唯一能推断出的事情就是是否匹配!将“roota”或“rootb”(其中“a”和“b”代表密码)放入哈希函数中会得到截然不同的输出。 - David Grant
攻击者可以使用彩虹表来破解盐值。 - Georg Schölly
服务器必须知道未加密的盐(以检查密码是否与哈希值匹配),因此可以认为攻击者可以访问盐或轻易获取它。 - Georg Schölly
没错,没错,这是肯定的 - 更具体地说,我指的是整个加密系统(或哈希子系统)的强度。 - AviD
@AviD: 因为使用用户名和密码拼接仍可能被智能彩虹表攻破,所以我建议最好使用随机字符序列作为盐,并将盐存储在同一位置。如果您愿意,您可以在用户每次登录时更改盐。 - David Grant

3

如果盐值是已知的或很容易猜到的,那么你并没有增加字典攻击的难度。甚至可能会创建一个修改后的彩虹表,考虑到“常量”盐。

使用唯一的盐可以增加批量字典攻击的难度。

拥有唯一的、密码学强度的盐值是理想的。


2
彩虹表的整个意义在于值是预先生成的,如果您为一个值(例如密码)加上给定的常量生成表格,那么这就是完全不同的表格,您可能会直接尝试哈希结果。 :) - David Grant
1
一个常量哈希值毫无价值。所有密码都可以使用单个彩虹表破解。盐的目的是使得创建彩虹表变得不可能。 - Georg Schölly
1
@gs - 我不明白为什么你不能构建一个包括常量盐的彩虹表。攻击空间(密码)没有改变,因此表格不需要增加,只需重新计算即可。 - HUAGHAGUAH
@Potato - 取决于权衡。如果你公司的20k个登录都使用相同的常量盐进行哈希处理,那么计算新的彩虹表可能比逐个字典攻击更便宜。因此我强调“批量”。 - HUAGHAGUAH

3
只要每个密码的盐值不同,您应该就可以了。盐的作用是防止使用标准彩虹表来解决数据库中的每个密码。因此,如果为每个密码应用不同的盐(即使它不是随机的),攻击者基本上必须为每个密码计算一个新的彩虹表,因为每个密码都使用不同的盐。
在这种情况下,使用更多熵的盐并不能帮助太多,因为假定攻击者已经拥有了数据库。由于您需要能够重新创建哈希,因此您必须已经知道盐是什么。因此,您无论如何都必须将盐或组成盐的值存储在文件中。在像Linux这样的系统中,获取盐的方法是已知的,因此拥有秘密盐没有用处。您必须假设拥有哈希值的攻击者可能也知道您的盐值。

3
“盐”的作用是防止使用标准的彩虹表来破解数据库中的每个密码。我不同意这种说法。它的作用是防止使用标准的彩虹表来破解任何密码。如果“盐”是用户名,那么“root”的彩虹表可以破解“root”的密码。 - Steve Jessop
这可能是唯一真正重要的规则。基本上,您希望密码在系统中是独特的,但它并不重要是什么。虽然它仍然可以是简单的东西。您不需要太多的熵。 - Kibbee
这也是我的想法 - 但我卡在了“可能没问题”的地方。我仍然没有看到这个理论被证明 - 或者至少验证了没有密码分析的影响。 - AviD
@onebyone:如果盐值由用户名+常量本地值(我经常使用“:”)组成,则仍会破坏标准化的RT,除非其中包括常见用户名和常见静态盐值的各种类型。我相当怀疑这不是情况。 - nsayer
除了nsayer之外,您可以使用系统范围的常量字符串,该字符串没有预生成的RT,例如#(D83d8,以用户名作为盐,这可能会防止任何彩虹表攻击。 - Kibbee

1

盐应具有尽可能多的熵值,以确保如果给定的输入值被多次哈希,生成的哈希值将始终是尽可能不同的。

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

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

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


再次强调,我不是指使用单个常量盐 - 而是使用非随机但用户唯一的值,例如用户ID。 - AviD
整个意义在于你的盐值永远不应该是恒定的。每一个需要散列的东西,无论是相同的“输入”值还是不同的,都应该有一个不同的盐值。Dave Sherohman解释了即使使用与用户相关的唯一值,这些值在每次散列计算中实际上始终保持不变的缺点。 - CraigTP

-1

熵是盐值的关键。

如果盐有一些简单且可重复的“数学”原理,那么它就好像没有盐一样。只需添加时间值即可。


我不理解这个注释。你说“使用熵”,然后又说:“使用时间”?? - Martin Carpenter
“足够熵”是主观的,这取决于您的标准。仅基于时间来确定此值可能不足够。 - Martin Carpenter
没有所谓的“绝对真正的随机性”。我们需要自己判断何时达到了“足够好”的程度。如果你认为在这种情况下时间不够好,那就使用其他方法吧。 - dmajkic
想象一个系统,每个月的第一天创建一批账户(随机密码)。想象它使用低粒度时间作为盐(或是一个非常快速的系统,每个时间单位可以创建数百个账户)。现在我们同时面临 AviD 的 (a) 和 (b) 攻击。 - Martin Carpenter
@AviD:如果我们把你的问题反过来,什么情况下使用高熵盐是不好的?额外的CPU周期?可预测的PRNG? - Martin Carpenter
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接