据我所知,生成盐的最佳做法是使用一些神秘的公式(甚至是魔数)存储在您的源代码中。
我正在开发一个计划作为开源发布的项目,但问题是随着源代码的公开,会泄漏生成盐的秘密公式,从而可能使我们的网站受到彩虹表攻击。
我想很多人在我之前已经思考过这个问题,想知道最佳实践是什么。对我来说,如果代码是开源的,就没有必要使用盐,因为盐可以很容易地被逆向工程破解。
你有什么想法吗?
据我所知,生成盐的最佳做法是使用一些神秘的公式(甚至是魔数)存储在您的源代码中。
我正在开发一个计划作为开源发布的项目,但问题是随着源代码的公开,会泄漏生成盐的秘密公式,从而可能使我们的网站受到彩虹表攻击。
我想很多人在我之前已经思考过这个问题,想知道最佳实践是什么。对我来说,如果代码是开源的,就没有必要使用盐,因为盐可以很容易地被逆向工程破解。
你有什么想法吗?
由于关于哈希加盐的问题经常出现,并且似乎有相当多的混淆,因此我扩展了这个答案。
盐是添加到哈希算法输入的一组固定长度的随机字节。
在哈希中添加随机的盐可以确保相同的密码产生许多不同的哈希值。盐通常与哈希函数的结果一起存储在数据库中。 对哈希进行加盐有许多好处:
盐值不是一个秘密密钥,而是通过使哈希函数特定于每个实例来“工作”。使用盐值哈希,不是一个哈希函数,而是每个可能的盐值对应一个哈希函数。这可以防止攻击者以少于攻击一个密码的N倍的代价攻击N个哈希密码。这就是盐的作用。
“秘密盐”不是盐,它被称为“密钥”,这意味着你不再计算哈希,而是计算消息认证码(MAC)。计算MAC是非常棘手的(比简单地将密钥和值组合成哈希函数要棘手得多),它是一个完全不同的主题。
盐必须对每个实例都是随机的。这确保攻击者必须分别攻击每个盐值哈希。
如果你依赖于你的盐值(或盐算法)是秘密的,那么你就进入了安全通过混淆的领域(不起作用)。最有可能的是,你并没有从盐值保密中获得额外的安全性;你只是从中获得了一种安全感。所以,与其使你的系统更加安全,它只会分散你对现实的注意力。
从技术上讲,盐应该是独特的。盐的作用是为每个哈希密码生成不同的标识,这是全球范围内的。由于没有中央组织可以按需分配唯一的盐,因此我们必须依赖于下一个最好的选择,即使用不可预测的随机生成器进行随机选择,最好在足够大的盐空间内进行,以使碰撞不太可能发生(两个实例使用相同的盐值)。
有时候人们会尝试从“可能是独特的”某些数据中派生出盐,例如用户ID,但此类方案通常失败,原因如下:
如果您使用例如用户ID,那么一些恶意人士攻击不同的系统时,可能只需汇集他们的资源并为用户ID 1到50创建预计算表。用户ID在整个系统中是唯一的,但在全球范围内不是。
用户名也是如此:root只有一个,但世界上有很多根用户。针对“root”的彩虹表将是值得一试的,因为它可以应用于数百万个系统。更糟糕的是,世界上也有许多“bob”,而且许多人没有系统管理员培训:他们的密码可能非常脆弱。
独特性还与时间有关。有时候,用户会更改密码。对于每个新密码,都必须选择一个新盐。否则,攻击者将获得旧密码的哈希值和新密码的哈希值,并尝试同时攻击两者。
使用从密码学安全、不可预测的PRNG获得的随机盐可能有点过度,但至少可以证明它能保护您免受所有这些危险。这不是防止攻击者知道单个盐是什么,而是不让他们得到将用于大量潜在目标的大而肥的目标。随机选择使目标尽可能细小。
使用随机、均匀分布、高熵盐。每次创建新密码或更改密码时使用新的盐。将盐与哈希密码一起存储。偏爱大盐(至少10字节,最好16字节或更多)。
盐不能将弱密码变成强密码。它只能确保攻击者为破解每个弱密码而付出词典攻击的代价。
有用的来源:
stackoverflow.com: 非随机盐用于密码哈希
Bruce Schneier: 实用密码学 (书)
Matasano Security: 够了,彩虹表
usenix.org: Unix crypt自1976年以来使用盐
owasp.org: 为什么要添加盐
openwall.com: 盐
免责声明:
我不是一名安全专家。(尽管该回答已由Thomas Pornin审核)
如果有任何安全专业人士发现错误,请在评论中指出或编辑此维基回答。
实际上,相对于每个条目而言,盐只需要是唯一的即可。 即使攻击者能够计算出盐是什么,它也使得彩虹表变得极其难以创建。这是因为盐在密码散列之前添加到密码中,因此实际上增加了彩虹表必须包含的条目总数,以获得密码字段的所有可能值列表。
在运行时,您可以为每个记录生成一个随机盐。例如,假设您正在将散列的用户密码存储在数据库中。您可以在运行时生成一个由小写和大写字母数字字符组成的 8 个字符的随机字符串,并将其放置在密码之前,对该字符串进行哈希处理,并将其存储在数据库中。由于可能的盐有 628 种,因此生成彩虹表(针对每个可能的盐)将非常昂贵;而且由于您为每个密码记录使用唯一的盐,即使攻击者已经生成了几个匹配的彩虹表,他仍然无法破解每个密码。
根据您的安全需求,您可以更改盐生成的参数;例如,您可以使用更长的盐,或者您可以生成一个还包含标点符号的随机字符串,以增加可能的盐数。
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和用户特定组件,您将获得不可破解的安全性!