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

bar*_*oon 31 security passwords hash salt

我之前关于PHP/MySQL中的盐渍密码的问题之后,我还有另外一个关于盐的问题.

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

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

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

Ric*_*ams 65

应为每个用户随机生成一个新的盐,并且每次更改密码时都应该这样.例如,不要仅仅依靠一个广泛的盐,因为它首先会破坏使用盐的程度.

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

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

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

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

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

您的用户记录现在应该具有格式化为" $<algorithm>$<iterations>$<salt>$<hash>"的字段(或者如果需要,可以作为单独的字段).

当用户输入密码时,您可以从本地文件中检索数据库中的盐和迭代次数以及站点密码,并验证当您使用salt和密码运行相同数量的迭代时,生成的哈希值与你已经存储了.

如果用户更改了密码,那么您应该生成一个新的盐.

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

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

  • 我建议不要使用固定长度的盐.原因是未来的变化.如果使用分隔符,则可以通过某些识别因子(例如,更改分隔符等)来检测"旧哈希"与"新哈希".所以你可以存储`$ salt.':'.$ hash.':'.$ hashFunc`来得到`abc:def:sha1`.这样,如果您以后决定切换到更长的盐,或更改哈希函数,您可以检测旧密码并"动态升级"... (3认同)
  • 是的,没有要求将它们存储在单独的列中,只要盐和最终哈希都以某种方式为每个用户提供,那么它们都可以正常工作.在一栏中将它像"sha256:salt:finalhash"一样存储似乎是一个受欢迎的选择. (2认同)
  • @The Rook:你假设网站容易受到SQL注入的影响,这是一个完全独立的问题.如果该站点对SQL注入是开放的,那么存储密码的问题要大得多.如果您在任何密码存储情况下都可以访问数据库,那么您就可以通过暴力获取明文.如果通过SQL注入完全访问数据库,您是否有一些不易受攻击的方法?你在这里提出什么样的替代方案? (2认同)