每个密码需要一次“随机盐”,每个数据库只需要一次吗?

时间:2010-06-08 16:19:17

标签: security passwords hash salt

my previous question about salted passwords in PHP/MySQL之后,我还有另一个关于盐的问题。

当有人说“使用随机盐”来预先/附加密码时,这是否意味着:

  • 创建静态1次随机生成的字符串
  • 每次创建一个随机更改的字符串 密码已创建

如果每个用户的盐是随机的并与散列密码一起存储,那么原始盐如何被检索回来进行验证?

4 个答案:

答案 0 :(得分:64)

应为每个用户随机生成一个新的盐,并且每次更改密码时都应该。例如,不要仅仅依靠一个广泛的盐,因为这首先打破了使用盐的目的。

为每个用户使用唯一的salt是这样的,如果两个用户拥有相同的密码,他们将无法获得相同的结果哈希值。这也意味着需要针对每个用户单独安装暴力攻击,而不是为网站预先计算rainbow table

然后,您将盐和密码的散列结果存储在数据库hash(salt + password)中,并为每个用户存储salt。您可以将它们存储在单独的列中,也可以存储在一列中(由散列中未使用的某个字符分隔,例如;)。只要你能找回两者就可以了。

但是,如果您的数据库受到损害,无论是由于某人获得本地访问权限还是通过SQL注入攻击,那么salt和final哈希都将可用,这意味着对用户密码的暴力攻击将是微不足道的。为了解决这个问题,正如The Rook所建议的那样,你也可以使用存储在本地文件中的全站点密钥作为哈希方法的另一个输入,这样攻击者也需要知道这一点以进行有效的攻击。这意味着您的数据库必须受到攻击,攻击者需要访问本地文件。所以使用hash(hash(salt + secret) + password)

虽然在大多数算法中,您的目标是尽可能快地进行操作,但是对于要减慢密码哈希的密码哈希,这称为Key Strengthening(或有时称为密钥拉伸)。如果您的哈希函数返回需要0.00001秒,则有人可以尝试每秒强制执行100,000个密码,直到找到匹配项。如果您的哈希函数需要1秒钟来吐出结果,那么就某人登录您的应用程序而言,这并不是什么大问题,但是为了破解密码这是一个更大的交易,因为每次尝试现在需要1秒才能获得结果,这意味着测试每个暴力强制密码需要花费100,000倍于使用原始散列函数的密码。

要使哈希函数更慢,您只需要多次运行它。例如,您可以执行new_hash = salt + password + previous_hash 100,000次。如果迭代次数太快,您可能需要将迭代次数调整为更高的值。如果您希望以后能够更改该值,请确保使用用户记录存储迭代次数,以免影响先前存储的任何密码。

您的用户记录现在应该有一个格式为“$<algorithm>$<iterations>$<salt>$<hash>”的字段(如果需要,可以作为单独的字段)。

当用户输入密码时,您可以从本地文件中检索数据库中的盐和迭代次数以及站点密码,并验证当您使用salt和密码运行相同数量的迭代时,结果哈希匹配您存储的内容。

如果用户更改了密码,则应生成新的盐。

您使用的散列方法无关紧要(但散列算法确实*)。上面我建议hash(hash(salt + secret) + password)但同样可能是hash(hash(salt) + hash(secret) + hash(password))。您使用的方法不会改变密码存储的有效性,实际上并不比其他方法更安全。依赖于如何将密码和盐一起散列以提供安全性的设计称为security through obscurity,应该避免使用。

*您不应使用MD5或SHA-1,因为这些被认为是不安全的。请改用SHA-2系列(SHA256,SHA512等)。 (Ref

答案 1 :(得分:4)

盐必须与哈希一起存储以便进行验证。最佳做法是每次创建或更改密码时使用不同的盐。

答案 2 :(得分:4)

第二种选择是正确的。

传统上,salt与散列密码一起存储,但未加密(通常是预先填充的,for example in unix passwords

更新:大多数较新的Unix系统中使用的方法是this one

答案 3 :(得分:2)

动态更换盐比使用一次性静盐更安全。

同样根据用户而不是一种全球盐使盐成为独特的。例如,每次用户登录时都要更改它们。

将盐连接到密码的末尾然后散列它是可以的,但这是一个容易猜测的公式。最好拿出你自己的一个,即编织salt和密码来创建一个新的字符串然后哈希,因为md5(密码+盐)仍然容易受到字典攻击。