Salt Generation和开源软件

use*_*085 70 security encryption open-source salt rainbowtable

据我了解,生成盐的最佳做法是使用源代码中存储的一些神秘公式(甚至魔术常量).

我正在研究一个我们打算作为开源发布的项目,但问题是源代码是生成盐的秘密公式,因此能够在我们的网站上运行彩虹表攻击.

我认为很多人在我面前都考虑过这个问题,我想知道最佳做法是什么.在我看来,如果代码是开源的话,根本就没有盐,因为盐可以很容易地进行逆向工程.

思考?

Jac*_*cco 227

由于关于盐渍哈希的问题经常出现,并且似乎对这个问题存在一些混淆,我扩展了这个答案.


什么是盐?

salt是固定长度的随机字节集,添加到散列算法的输入中.


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

向散列添加随机盐可确保相同的密码将产生许多不同的散列.salt通常与散列函数的结果一起存储在数据库中.由于以下几个原因,对哈希进行盐析是很好的:

  1. 腌制大大增加了预计算攻击的难度/成本(包括彩虹表)
  2. Salting确保相同的密码不会产生相同的哈希值.这可确保您无法确定两个用户是否具有相同的密码.而且,更重要的是,您无法确定同一个人是否在不同系统中使用相同的密码.
  3. Salting增加了密码的复杂性,从而大大降低了Dictionary Birthday攻击的有效性.(如果盐这是唯一真正存储从哈希分开).
  4. 适当的腌制大大增加了预计算攻击的存储需求,直至它们不再实用.(8个字符区分大小写的字母数字密码,16位盐,散列到128位值, 没有彩虹减少的情况,将占用不到200 艾字节).


盐不需要保密.

salt不是一个秘密密钥,而是通过使哈希函数特定于每个实例来使"盐"起作用.使用salted哈希,没有一个哈希函数,但每个可能的盐值都有一个哈希函数.这可以防止攻击者攻击N个散列密码的次数少于攻击一个密码的N倍.这是盐的重点.
"秘密盐"不是盐,它被称为"密钥",它意味着您不再计算哈希值,而是计算消息验证码(MAC).计算MAC是一项棘手的业务(比简单地将一个键和一个值拼接到一个哈希函数中要复杂得多)并且它完全是一个非常不同的主题.

对于使用它的每个实例,盐必须是随机的.这可确保攻击者必须分别攻击每个salted哈希.
如果你依赖你的盐(或盐算算法)是秘密的,你进入安全通过晦涩的领域(将无法工作).最有可能的是,你没有从盐保密中获得额外的安全性; 你只是得到了温暖的模糊安全感.因此,它不会让您的系统更安全,而只会让您远离现实.


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

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

尝试从一些"大概是唯一的"数据中获取盐是很诱人的,例如用户ID,但是由于一些令人讨厌的细节,这些方案经常会失败:

  1. 如果您使用例如用户ID,则攻击不同系统的一些坏人可能只汇集其资源并为用户ID 1到50创建预先计算的表.用户ID在系统范围是唯一的,但不是全球性的.

  2. 这同样适用于用户名:每个Unix系统都有一个"root",但世界上有很多根."root"的彩虹表值得付出努力,因为它可以应用于数百万个系统.更糟糕的是,那里还有很多"bob",很多人没有系统管理员培训:他们的密码可能很弱.

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

使用从加密安全,不可预测的PRNG获得的随机盐可能是某种矫枉过正,但至少它可以证明可以保护您免受所有这些危害.这不是为了防止攻击者知道单个盐是什么,而是为了不给他们提供大量,可用于大量潜在目标的大目标.随机选择使目标尽可能薄.


结论:

使用随机,均匀分布的高熵盐.无论何时创建新密码或更改密码,都要使用新的盐.将盐与散列密码一起存储.喜欢大盐(至少10个字节,最好是16个或更多).

盐不会将错误的密码变成好的密码.它只是确保攻击者至少为他打破的每个错误密码支付字典攻击价格.


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

免责声明:
我不是安全专家.(虽然这个答案由Thomas Pornin审查)
如果有任何安全专业人员发现错误,请评论或编辑此维基答案.

  • 对于系统使用的所有机器的每一行,它需要是不同的,盐也需要是不可预测的.这是通过创建随机盐来实现的.另见:http://stackoverflow.com/questions/536584/non-random-salt-for-password-hashes/536756#536756 (4认同)
  • 如果您知道salt,则可以对此特定哈希进行暴力攻击.如果要打破单个哈希,创建彩虹表没有任何优势.此外,为良好的哈希方案创建一百万个哈希将花费比您想象的更长的时间. (3认同)
  • 另一点在这里更相关.你说"技术上,盐应该是独一无二的",但这还不够强大.正如你在其他地方所说的那样,它也需要大量的熵.如果它只是由一些"中央组织"发放的,它们可能只是分配顺序盐,并且知道迄今已分配了多少盐,或者哪些盐已经到达目标,将减少攻击空间.预先计算的攻击. (2认同)

kem*_*002 18

真正的盐只需要每个条目都是唯一的.即使攻击者可以计算盐的含量,也会使彩虹表极难创建.这是因为salt在被散列之前被添加到密码中,因此它有效地增加了彩虹表必须包含的条目总数,以获得密码字段的所有可能值的列表.


Dav*_*ley 7

由于Unix变得流行,存储密码的正确方法是附加一个随机值(盐)并散列它.将盐分保存在以后可以到达的地方,但是你希望那些坏人不会得到它.

这有一些很好的效果.首先,坏人不能只列出预期的密码,如"Password1",将它们哈希到彩虹表中,然后通过密码文件查找匹配项.如果你有一个很好的双字节盐,他们必须为每个预期的密码生成65,536个值,这使得彩虹表不那么实用.其次,如果你能从查看密码文件的坏人那里得到盐,你就会更难以计算出可能的值.第三,你已经让坏人无法确定某个人是否在不同的网站上使用相同的密码.

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

如果你有复杂的计算来制盐,那你做错了.如果你根据密码计算它,那你就错了.在这种情况下,你所做的只是使散列复杂化,而不是在功能上添加任何盐.

没有人擅长安全性会依赖于隐藏算法.现代密码学基于已经过广泛测试的算法,并且为了进行广泛测试,它们必须是众所周知的.一般来说,人们发现使用标准算法比使用标准算法更安全,并希望它是好的.如果代码是开源的并不重要,那么坏人通常仍然可以分析程序的功能.