据我了解,生成盐的最佳实践是使用一些存储在源代码中的神秘公式(甚至魔术常数)。
我正在开发一个计划以开源形式发布的项目,但是问题在于,该来源附带了生成盐的秘密公式,因此可以在我们的网站上进行彩虹表攻击。
我认为很多人在我之前已经考虑了这个问题,我想知道最佳实践是什么。在我看来,如果代码是开源的,就根本没有必要添加盐,因为可以很容易地对盐进行逆向工程。
有什么想法吗?
Answers:
确实,盐对于每个条目都需要唯一。即使攻击者可以计算出盐的含量,也很难创建彩虹表。这是因为盐是在散列之前添加到密码中的,因此它有效地增加了彩虹表必须包含的条目总数,以便具有密码字段的所有可能值的列表。
由于有关盐渍哈希的问题会定期出现,并且对此主题似乎有些混乱,因此我扩展了此答案。
盐是添加到哈希算法输入中的固定长度的随机字节集。
在哈希中添加随机盐可以确保相同的密码将产生许多不同的哈希。盐通常与哈希函数的结果一起存储在数据库中。对哈希加盐有很多好处,原因如下:
盐不是秘密密钥,而是盐通过使哈希函数特定于每个实例而“起作用”。对于带盐分的哈希,没有一个哈希函数,但是每个可能的盐值都有一个。这样可以防止攻击者以少于攻击一个密码的成本的N倍的价格攻击N个哈希密码。这就是盐的要点。
“秘密盐”不是盐,它被称为“密钥”,它意味着您不再计算哈希,而是消息认证码(MAC)。计算MAC是一项棘手的业务(比简单地将一个键和一个值组合在一起成为哈希函数要棘手得多),并且这是一个完全不同的主题。
对于使用盐的每个实例,盐必须是随机的。这样可以确保攻击者必须分别攻击每个加盐的哈希。
如果您依靠自己的盐(或盐化算法)保密,那么您将进入“通过隐蔽性实现安全性”的领域(将不起作用)。最有可能的是,您不会从盐保密性中获得额外的安全性。您只会得到温暖的模糊安全感。因此,与其使您的系统更加安全,不如让您从现实中分散注意力。
从技术上讲,盐应该是唯一的。对于每个散列密码,盐的要点是不同的。这意味着全世界。由于没有中央组织根据需要分配独特的盐,因此我们必须依靠下一个最好的方法,那就是使用不可预测的随机生成器进行随机选择,最好是在一个足够大的盐空间内以使碰撞不可能发生(两个实例使用相同的实例盐值)。
试图从一些“大概唯一的”数据(例如用户ID)中得出一个盐是很诱人的,但是由于某些令人讨厌的细节,此类方案通常会失败:
例如,如果您使用用户ID,则某些恶意分子攻击不同的系统,他们可能会合并其资源并为用户ID 1至50创建预先计算的表。用户ID在系统范围内是唯一的,但在全球范围内不是唯一的。
这同样适用于用户名:有一个“根”每个Unix系统中,但也有在世界上许多根。用于“根”的彩虹表值得付出努力,因为它可以应用于数百万个系统。更糟糕的是,那里还有许多“鲍勃”,而且许多人没有接受系统管理员培训:他们的密码可能很弱。
唯一性也是暂时的。有时,用户更改密码。对于每个新密码,必须选择一个新的盐。否则,攻击者会获得旧密码的哈希值,而新密码的哈希值可能会尝试同时攻击两者。
使用从加密安全的,不可预测的PRNG中获得的随机盐可能有点过分,但至少可以证明它可以保护您免受所有这些危害。这并不是要防止攻击者知道单个盐是什么,而是要给他们提供不会用于大量潜在目标的大型肥胖目标。随机选择使目标尽可能地薄。
使用随机,均匀分布的高熵盐。每当您创建新密码或更改密码时,请使用新盐。将盐和哈希密码一起存储。支持大盐(至少10个字节,最好是16个或更多)。
盐不会将错误的密码转换为正确的密码。它只是确保攻击者至少会为他破解的每个错误密码付出字典攻击的代价。
有用的来源:
stackoverflow.com:用于密码哈希的非随机盐
Bruce Schneier:实用密码学(书)
Matasano安全性:足够使用Rainbow Tables
usenix.org:Unix crypt自1976年以来使用了盐
owasp.org:为什么要添加盐
openwall.com:盐
免责声明:
我不是安全专家。(尽管此答案已由Thomas Pornin审查过)
如果任何安全专家在那里发现错误,请发表评论或编辑此Wiki答案。
自从Unix流行以来,正确的存储密码的方法就是添加一个随机值(盐)并对其进行哈希处理。将盐保存起来,以后可以使用,但是希望坏人不会得到。
这有一些好的效果。首先,坏人不能只是列出期望的密码列表,例如“ Password1”,将其哈希到彩虹表中,然后遍历您的密码文件以查找匹配项。如果您有一个很好的2字节盐,那么它们必须为每个预期的密码生成65,536个值,这会使Rainbow表不那么实用。其次,如果您可以避免那些正在查看密码文件的坏人,这会使计算可能的值变得更加困难。第三,您使坏蛋无法确定给定的人是否在不同的站点上使用相同的密码。
为此,您将生成随机盐。这应该以均匀的概率生成所需范围内的每个数字。这并不困难;一个简单的线性同余随机数生成器就可以很好地工作。
如果制作盐的计算复杂,则说明您做错了。如果您根据密码进行计算,那是错误的做法。在这种情况下,您要做的就是使哈希复杂化,而不是在功能上添加任何盐。
擅长安全性的人都不会依赖隐藏算法。现代密码学基于已经过广泛测试的算法,并且要进行广泛测试,它们必须是众所周知的。通常,已经发现使用标准算法比自己动手并希望它更好是更安全的。代码是否是开源都没有关系,坏人仍然经常可以分析程序的功能。
您可以在运行时为每个记录生成随机盐。例如,假设您要将散列的用户密码存储在数据库中。您可以在运行时生成8个字符的大小写字母数字字符的随机字符串,将其添加到密码之前,对该字符串进行哈希处理,然后将其存储在数据库中。由于有62 8 8种可能的盐,因此生成彩虹表(每种可能的盐)将非常昂贵。而且由于您为每个密码记录使用了唯一的盐,即使攻击者生成了几个匹配的Rainbow表,他仍然无法破解每个密码。
您可以根据安全需要更改盐生成的参数。例如,您可以使用更长的盐,或者可以生成还包含标点符号的随机字符串,以增加可能的盐数量。
使用随机函数生成器生成盐,并将其存储在数据库中,每行加盐一次,然后将其存储在数据库中。
我喜欢在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和用户特定的组件,您将拥有坚不可摧的安全性!