我将提供一个稍微不同的看法。
我总是将盐与加盐密码哈希混合在一起存储。
例如,我会将盐的前半部分放在密码的盐渍散列之前,将盐的后半部分放在密码的盐渍散列之后。应用程序知道这种设计,因此可以获取此数据,并获得盐和盐渍密码哈希。
我采用这种方法的理由:
如果密码/哈希数据被泄露并落入攻击者手中,攻击者将无法通过查看数据知道盐是什么。这样,攻击者实际上无法执行暴力攻击来获得与哈希匹配的密码,因为他不知道哈希的开头,也无法知道数据的哪些部分是盐的一部分,或者加盐密码哈希的一部分(除非他确实知道您的应用程序的身份验证逻辑)。
如果加盐密码散列按原样存储,则可以执行蛮力攻击以获得一个密码,该密码在加盐和散列时产生与加盐密码散列相同的数据。
但是,例如,即使加盐密码哈希按原样存储,但预先附加了单个随机字节,只要攻击者不知道要丢弃第一个字节,这也会增加难度的攻击。当您的应用程序用于验证您的用户时,您的应用程序会知道丢弃数据的第一个字节。
这个结论..
1) 永远不要以准确的形式存储您的身份验证应用程序使用的数据。
2) 如果可能,将您的身份验证逻辑保密以增加安全性。
更进一步。。
如果您不能对应用程序的身份验证逻辑保密 - 很多人都知道您的数据是如何存储在数据库中的。并且假设您决定将加盐密码散列与盐混合在一起存储,其中一些盐在加盐密码散列之前,其余的加盐将其附加。
生成随机盐时,您还可以随机决定在加盐密码哈希之前/之后存储的盐的比例。
例如,您生成一个 512 字节的随机盐。您将盐附加到您的密码中,并获取您的加盐密码的 SHA-512 哈希。您还生成一个随机整数 200。然后存储 salt 的前 200 个字节,然后是 salted-password 哈希,然后是 salt 的其余部分。
在验证用户的密码输入时,您的应用程序将传递字符串,并假设数据的前 1 个字节是 salt 的前 1 个字节,然后是 salted-hash。此通行证将失败。应用程序将继续使用数据的前 2 个字节作为 salt 的前 2 个字节,并重复使用前 200 个字节作为 salt 的前 200 个字节后找到肯定的结果。如果密码错误,应用程序将继续尝试所有排列,直到找不到。
这种方法的优点:
提高安全性 - 即使您的身份验证逻辑是已知的,确切的逻辑在编译时也是未知的。即使知道确切的逻辑,也几乎不可能执行暴力攻击。增加盐的长度将进一步提高安全性。
这种方法的缺点:
由于在运行时推断出确切的逻辑,因此这种方法非常占用 CPU。salt 的长度越长,这种方法的 CPU 密集度就越高。
验证不正确的密码将涉及最高的 CPU 成本。这对合法请求可能会适得其反,但会增加对攻击者的安全性。
这种方法可以以多种方式实现,并且可以通过使用可变宽度的盐和/或加盐密码散列变得更加安全。