盐生成和开源软件

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

我正在开发一个我们计划以开源方式发布的项目,但问题在于,源代码是生成盐类的秘密公式,因此可以在我们的网站上运行彩虹表攻击。

我认为,很多人在我面前都考虑过这个问题,我想知道最佳实践是什么。 在我看来,如果代码是开源的,那么根本就没有任何意义,因为盐可以很容易地被反向工程化。

思考?

真的盐只需要为每个条目都是独一无二的。 即使攻击者可以计算出盐的含量,也会让彩虹桌非常难以制造。 这是因为salt在散列之前被添加到密码中,所以它有效地增加了彩虹表必须包含的条目的总数,以具有密码字段的所有可能值的列表。

由于关于腌制哈希的问题经常出现,而且似乎对这个问题有一些困惑,所以我扩展了这个答案。

什么是盐?

salt是随机的一组固定长度的字节,添加到散列algorithm的input中。

为什么腌制(或播种)散列有用?

向散列添加一个随机盐可以确保相同的密码会产生许多不同的散列。 盐通常与散列函数的结果一起存储在数据库中。 抨击散列是有好处的一些原因:

  1. 盐化大大增加了预计算攻击的难度/成本(包括彩虹表 )
  2. Salting确保相同的密码不会导致相同的散列。 这确保您不能确定两个用户是否具有相同的密码。 而且, 更重要的是 ,您不能确定同一个人是否在不同的系统上使用相同的密码。
  3. 腌制增加了密码的复杂性,从而大大降低了 字典 生日攻击 的有效性 (这只有在盐与散列分开存储的情况下才是真实的)。
  4. 适当的腌制大大增加了预计算攻击的存储需求,直到它们不再实用。 (8字符区分大小写的字母数字密码与16位盐,哈希到128位值,将占用不超过200 艾字节,而不减less彩虹)。

盐不需要保密。

盐不是一个秘密密钥,而是通过使每个实例的散列函数具有特定的含义来工作。 使用盐腌散列,没有一个散列函数,但是对于每个可能的盐值。 这样可以防止攻击者攻击N个散列密码的次数小于N次攻击密码的成本。 这是盐的重点。
一个“秘密盐”不是一个盐,它被称为“钥匙”,这意味着你不再计算哈希,而是一个消息authentication码 (MAC)。 计算MAC是一项棘手的业务(比简单地将一个密钥和一个值集中到一个哈希函数中要复杂得多),而这完全是一个非常不同的主题。

对于使用它的每个实例,盐必须是随机的。 这确保了攻击者必须分别攻击每个腌制的散列。
如果你依赖于你的盐(或者腌制algorithm)是秘密的,你进入了默默无闻的安全领域(不起作用)。 最有可能的是,你没有从盐的保密中获得额外的安全保障。 你只是得到了温暖的模糊的安全感。 所以不是让你的系统更安全,而是把你从现实中分离出来。

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

从技术上讲,盐应该是独一无二的 。 对于每个哈希密码,盐的点是不同的。 这意味着全世界 。 由于没有中央组织根据需要分配独特的盐,所以我们不得不依靠下一个最好的事情,即随机select一个不可预测的随机生成器,最好在一个足够大的盐空间内使碰撞不可能发生(使用相同的两个实例盐值)。

试图从一些“大概是独一无二的”数据(例如用户ID)中推导出一个salt,但是这样的scheme往往因为一些令人讨厌的细节而失败:

  1. 例如,如果您使用用户标识 ,则某些攻击不同系统的坏人可能只是汇集资源并为用户标识1至50创build预先计算的表。用户标识是系统范围 唯一的,但不是全球范围的

  2. 这同样适用于用户名 :每个Unix系统有一个“root”,但世界上有很多根。 彩虹表“根”是值得的,因为它可以应用于数百万的系统。 更糟糕的是,还有很多“bob”,很多没有系统pipe理员的培训:密码可能相当薄弱。

  3. 唯一性也是暂时的。 有时用户更改密码。 对于每个新密码 ,必须select新的盐 。 否则,攻击者获得旧密码的散列,新散列可能试图同时攻击。

使用从密码安全,不可预知的PRNG获得的随机盐可能是某种矫枉过正的,但至less它可以保护你免受所有这些危害。 这并不是要阻止攻击者知道什么是一个单独的盐,而是为了不给他们一个大的,胖的目标,将用于大量的潜在目标。 随机select使得目标尽可能地薄。

结论是:

使用随机均匀分布的高熵盐。 每当您创build新密码或更改密码时,请使用新盐。 将盐与散列的密码一起存储。 赞成大盐(至less10个字节,最好16个或更多)。

盐不会把一个错误的密码变成一个好的密码。 它只是确保攻击者至less会支付他破解的每个密码的字典攻击价格。

有用的来源:
stackoverflow.com: 密码哈希的非随机盐
布鲁斯·施奈尔: 实用密码学 (书)
Matasano安全: 足够与彩虹桌
usenix.org: 自从1976年Unix密码使用盐
owasp.org : 为什么要加盐
openwall.com :

免责声明:
我不是安全专家。 (虽然这个答案由Thomas Pornin审查)
如果有任何安全专家发现有问题,请发表评论或编辑这个维基答案。

由于Unixstream行起来,存储密码的正确方法是添加一个随机值(salt)并将其散列。 以后可以把盐放在你可以到的地方,但是你希望那些坏人不会得到它。

这有一些很好的效果。 首先,坏人不能只是列出像“Password1”这样的预期密码,将它们拼凑成彩虹表,然后通过密码文件查找匹配项。 如果你有一个好的双字节盐,他们必须为每个预期的密码产生65,536个值,这使得彩虹表不太实用。 其次,如果你可以保留那些正在查看你的密码文件的坏人的盐,那么你就很难计算出可能的值。 第三,你已经让坏人无法确定一个特定的人是否在不同的网站上使用相同的密码。

为了做到这一点,你生成一个随机的盐。 这应该以一致的概率产生所需范围内的每个数字。 这并不难; 一个简单的线性同余随机数发生器将很好地做。

如果你有复杂的计算来制造盐,你做错了。 如果你根据密码进行计算,你会这样做。 在这种情况下,你所做的只是使散列复杂化,而不是在function上添加任何盐。

没有人擅长安全将依靠隐藏algorithm。 现代密码学基于经过广泛testing的algorithm,为了进行广泛的testing,他们必须是众所周知的。 一般来说,使用标准algorithm比单独使用并期望它更好是比较安全的。 不pipe代码是否是开源的,对于坏人来说,分析一个程序的function往往是可能的。

您可以在运行时为每个logging生成随机盐。 例如,假设你将散列的用户密码存储在数据库中。 您可以在运行时生成一个由8个字符组成的小写和大写字母数字字符的随机string,将其添加到密码中, 对该string进行散列,并将其存储在数据库中。 由于有628种可能的盐,生成彩虹餐桌(每种可能的盐)将会过于昂贵; 而且由于每个密码logging都使用了独特的盐,即使攻击者已经生成了一对匹配的彩虹表,他仍然无法破解每个密码。

您可以根据您的安全需求更改盐生成的参数; 例如,您可以使用更长的盐,也可以生成一个随机string,其中也包含标点符号,以增加可能的盐的数量。

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

我喜欢在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本身是强大而牢不可破的。 添加多个维度来生成盐本身,随机数,沙和用户特定的组件, 你有牢不可破的安全!

对于桌面应用程序来说,在远程服务器上encryption数据并将其发送,您如何考虑每次使用不同的盐?

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

如果远程服务器一定不知道用户的密码,可以每次使用不同的盐吗? 如果用户在另一台计算机上使用桌面应用程序,如果他没有密钥(在软件中未硬编码),如何解密远程服务器上的数据?