盐生成和开源软件


74

据我了解,生成盐的最佳实践是使用一些存储在源代码中的神秘公式(甚至魔术常数)。

我正在开发一个计划以开源形式发布的项目,但是问题在于,该来源附带了生成盐的秘密公式,因此可以在我们的网站上进行彩虹表攻击。

我认为很多人在我之前已经考虑了这个问题,我想知道最佳实践是什么。在我看来,如果代码是开源的,就根本没有必要添加盐,因为可以很容易地对盐进行逆向工程。

有什么想法吗?

Answers:


20

确实,盐对于每个条目都需要唯一。即使攻击者可以计算出盐的含量,也很难创建彩虹表。这是因为盐是在散列之前添加到密码中的,因此它有效地增加了彩虹表必须包含的条目总数,以便具有密码字段的所有可能值的列表。


240

由于有关盐渍哈希的问题会定期出现,并且对此主题似乎有些混乱,因此我扩展了此答案。


什么是盐?

盐是添加到哈希算法输入中的固定长度的随机字节集。


为什么盐析(或播种)哈希有用?

在哈希中添加随机盐可以确保相同的密码将产生许多不同的哈希。盐通常与哈希函数的结果一起存储在数据库中。对哈希加盐有很多好处,原因如下:

  1. 加盐会大大增加预计算攻击(包括彩虹表)的难度/成本
  2. 盐析确保相同的密码不会导致相同的哈希。这样可以确保您无法确定两个用户是否具有相同的密码。而且,更重要的是,您无法确定同一个人是否在不同系统上使用相同的密码。
  3. 盐化会增加密码的复杂性,从而大大降低字典攻击生日攻击的有效性(如果盐这是唯一真正存储从哈希分开)。
  4. 适当的加盐处理会大大增加对预计算攻击的存储需求,直到不再实用为止。(8个字符的区分大小写的区分大小写的字母数字密码(散列为128位的值,含16位盐) 不减少彩虹的情况将占用不到200艾字节)。


不需要盐是秘密的。

盐不是秘密密钥,而是盐通过使哈希函数特定于每个实例而“起作用”。对于带盐分的哈希,没有一个哈希函数,但是每个可能的盐值都有一个。这样可以防止攻击者以少于攻击一个密码的成本的N倍的价格攻击N个哈希密码。这就是盐的要点。 “秘密盐”不是盐,它被称为“密钥”,它意味着您不再计算哈希,而是消息认证码(MAC)。计算MAC是一项棘手的业务(比简单地将一个键和一个值组合在一起成为哈希函数要棘手得多),并且这是一个完全不同的主题。

对于使用盐的每个实例,盐必须是随机的。这样可以确保攻击者必须分别攻击每个加盐的哈希。
如果您依靠自己的盐(或盐化算法)保密,那么您将进入“通过隐蔽性实现安全性”的领域(将不起作用)。最有可能的是,您不会从盐保密性中获得额外的安全性。您只会得到温暖的模糊安全感。因此,与其使您的系统更加安全,不如让您从现实中分散注意力。


那么,为什么盐必须是随机的?

从技术上讲,盐应该是唯一的。对于每个散列密码,盐的要点是不同的。这意味着全世界。由于没有中央组织根据需要分配独特的盐,因此我们必须依靠下一个最好的方法,那就是使用不可预测的随机生成器进行随机选择,最好是在一个足够大的盐空间内以使碰撞不可能发生(两个实例使用相同的实例盐值)。

试图从一些“大概唯一的”数据(例如用户ID)中得出一个盐是很诱人的,但是由于某些令人讨厌的细节,此类方案通常会失败:

  1. 例如,如果您使用用户ID,则某些恶意分子攻击不同的系统,他们可能会合并其资源并为用户ID 1至50创建预先计算的表。用户ID在系统范围是唯一的,在全球范围内不是唯一的。

  2. 这同样适用于用户名:有一个“根”每个Unix系统中,但也有在世界上许多根。用于“根”的彩虹表值得付出努力,因为它可以应用于数百万个系统。更糟糕的是,那里还有许多“鲍勃”,而且许多人没有接受系统管理员培训:他们的密码可能很弱。

  3. 唯一性也是暂时的。有时,用户更改密码。对于每个新密码,必须选择一个新的盐。否则,攻击者会获得旧密码的哈希值,而新密码的哈希值可能会尝试同时攻击两者。

使用从加密安全的,不可预测的PRNG中获得的随机盐可能有点过分,但至少可以证明可以保护您免受所有这些危害。这并不是要防止攻击者知道单个盐是什么,而是要给他们提供不会用于大量潜在目标的大型肥胖目标。随机选择使目标尽可能地薄。


结论:

使用随机,均匀分布的高熵盐。每当您创建新密码或更改密码时,请使用新盐。将盐和哈希密码一起存储。支持大盐(至少10个字节,最好是16个或更多)。

盐不会将错误的密码转换为正确的密码。它只是确保攻击者至少会为他破解的每个错误密码付出字典攻击的代价。


有用的来源:
stackoverflow.com:用于密码哈希的非随机盐
Bruce Schneier:实用密码学(书)
Matasano安全性:足够使用Rainbow Tables
usenix.org:Unix crypt自1976年以来使用了盐
owasp.org为什么要添加盐
openwall.com

免责声明:
我不是安全专家。(尽管此答案已由Thomas Pornin审查过)
如果任何安全专家在那里发现错误,请发表评论或编辑此Wiki答案。


4
对于使用该系统的所有机器,每一行的数据都需要不同,盐也必须是不可预测的。这是通过产生无规盐来实现的。另请参阅:stackoverflow.com/questions/536584/…–
雅科

1
@Jacco,这太棒了。我同意你关于随机性的观点。这是防止攻击者在所有系统中“猜测”盐分的最有效方法。(尽管加密RNG绝对是过大的)。
杰里米·鲍威尔,

3
如果您知道盐,则可以对此特定的哈希值进行蛮力攻击。如果要破坏单个哈希,创建彩虹表没有任何优势。同样,为一个好的哈希方案创建一百万个哈希将比您想象的要花费更长的时间。
Jacco 2010年

1
感谢您的快速回复。您是否认为弱密码并不常见,或者自适应成本哈希算法不是一个好主意?我很欣赏不偏离此问题重点的好处,但是我看到了很多关于密码哈希的不良做法和建议,我认为至少链接到算法方面的另一个好答案将非常有帮助。您知道关于stackoverflow的信息吗?在新的IT安全stackexchange,security.stackexchange.com/questions/211/password-hashing是相当不错的,所以从这里链接到它是帮助人们以正确的方式。
nealmcb 2011年

2
在此更相关的另一点。您说“从技术上讲,盐应该是唯一的”,但这还不够浓。正如您在其他地方所说的,它确实也需要很多熵。如果它是唯一的,并且由某些“中央组织”分发,则它们可能只是分配顺序的盐,而知道迄今为止分配了多少盐,或者哪些盐已到达目标的知识,将减少攻击的空间。预计算攻击。
nealmcb

7

自从Unix流行以来,正确的存储密码的方法就是添加一个随机值(盐)并对其进行哈希处理。将盐保存起来,以后可以使用,但是希望坏人不会得到。

这有一些好的效果。首先,坏人不能只是列出期望的密码列表,例如“ Password1”,将其哈希到彩虹表中,然后遍历您的密码文件以查找匹配项。如果您有一个很好的2字节盐,那么它们必须为每个预期的密码生成65,536个值,这会使Rainbow表不那么实用。其次,如果您可以避免那些正在查看密码文件的坏人,这会使计算可能的值变得更加困难。第三,您使坏蛋无法确定给定的人是否在不同的站点上使用相同的密码。

为此,您将生成随机盐。这应该以均匀的概率生成所需范围内的每个数字。这并不困难;一个简单的线性同余随机数生成器就可以很好地工作。

如果制作盐的计算复杂,则说明您做错了。如果您根据密码进行计算,那是错误的做法。在这种情况下,您要做的就是使哈希复杂化,而不是在功能上添加任何盐。

擅长安全性的人都不会依赖隐藏算法。现代密码学基于已经过广泛测试的算法,并且要进行广泛测试,它们必须是众所周知的。通常,已经发现使用标准算法比自己动手并希望它更好是更安全的。代码是否是开源都没有关系,坏人仍然经常可以分析程序的功能。


1
+1表示“无法确定给定的人是否在不同的站点上使用相同的密码”。
雅科

1

您可以在运行时为每个记录生成随机盐。例如,假设您要将散列的用户密码存储在数据库中。您可以在运行时生成8个字符的大小写字母数字字符的随机字符串,将其添加到密码之前,对该字符串进行哈希处理,然后将其存储在数据库中。由于有62 8 8种可能的盐,因此生成彩虹表(每种可能的盐)将非常昂贵。而且由于您为每个密码记录使用了唯一的盐,即使攻击者生成了几个匹配的Rainbow表,他仍然无法破解每个密码。

您可以根据安全需要更改盐生成的参数。例如,您可以使用更长的盐,或者可以生成还包含标点符号的随机字符串,以增加可能的盐数量。


然后,您必须将盐和哈希密码一起存储在数据库中,对吗?
user199085

2
如果将盐类存储在单独的数据库中,则将获得额外的保护,但是即使将盐类与散列密码一起存储,仅使用salt方法也将大大增加成功攻击的复杂性。这里的关键是,通过在哈希生成过程中包含一个随机元素,使破解所有密码变得更加困难。有关更多信息,请参见此
Dave R.

0

使用随机函数生成器生成盐,并将其存储在数据库中,每行加盐一次,然后将其存储在数据库中。

我喜欢在Django注册中如何生成盐。参考:http : //bitbucket.org/ubernostrum/django-registration/src/tip/registration/models.py#cl-85

salt = sha_constructor(str(random.random())).hexdigest()[:5]
activation_key = sha_constructor(salt+user.username).hexdigest()
return self.create(user=user,
           activation_key=activation_key)

他使用由随机数生成的sha和用户名的组合来生成哈希。

Sha本身以坚固和坚不可摧而闻名。添加多个维度来生成盐本身,并具有随机数,sha和用户特定的组件,您将拥有坚不可摧的安全性!


7
牢不可破的安全性?我认为这有点太乐观了。
雅科

6
SHA-1已损坏:schneier.com/blog/archives/2005/02/sha1_broken.html,因此请使用SHA-256。
雅科

2
SHA-256现在也已过时。当前建议的方法是BCrypt和PBKDF2。
格兰纳兹2014年

0

对于使用桌面应用程序加密数据并将其发送到远程服务器的情况,您如何考虑每次使用不同的盐?

使用带有用户密码的PKCS#5,它需要一个盐来生成加密密钥,以对数据进行加密。我知道在桌面应用程序中对盐进行硬编码(混淆)不是一个好主意。

如果远程服务器必须从不知道用户密码,那么每次可以使用不同的密码吗?如果用户在另一台计算机上使用桌面应用程序,那么如果他没有密钥(该密钥未在软件中进行硬编码),它将如何能够解密远程服务器上的数据?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.