我这里提供一个略有不同的看法。
我总是将盐和经过盐加密的密码哈希值混合存储在一起。
例如,我会将盐的前半部分放在密码盐哈希之前,将盐的后半部分放在密码盐哈希之后。应用程序知道这个设计,因此可以获取这些数据,并获取盐和经过盐加密的密码哈希值。
我的做法理由:
如果密码/哈希数据被攻击者窃取,攻击者无法仅从数据中查看盐。这样,攻击者不能实际执行暴力破解攻击以获取与哈希匹配的密码,因为他不知道哈希值,也无法知道哪些部分是盐的组成部分或经过盐加密的密码哈希值的组成部分(除非他知道您的应用程序身份验证逻辑)。
如果将经过盐加密的密码哈希值存储为原样,那么可以执行暴力破解攻击以获取一个密码,该密码通过盐加密和哈希后产生与经过盐加密的密码哈希值相同的数据。
但是,例如,即使将经过盐加密的密码哈希值存储为原样,但是在前面添加了一个随机字节,只要攻击者不知道这个字节应该被丢弃,这也会增加攻击的难度。您的应用程序会知道在用于身份验证用户时丢弃数据的第一个字节。
得出结论..
1)永远不要以精确形式存储身份验证应用程序使用的数据。
2)如果可能,请保持您的身份验证逻辑保密以提高安全性。
再深入一步..
如果无法保密您的应用程序身份验证逻辑 - 许多人知道数据库中存储数据的方式。并且假设您已经决定将经过盐加密的密码哈希值与盐混合存储在一起,并且部分盐将前缀添加到经过盐加密的密码哈希之前,其余部分将后缀添加到其中。
生成随机盐时,您还可以随机决定存储盐的哈希值之前/之后的比例。
例如,您生成了一个512字节的随机盐。将盐附加到密码上,并获取盐值密码的SHA-512哈希。您还生成了一个200的随机整数。然后,您存储前200个字节的盐,紧接着是盐值密码哈希和剩余的盐。
在验证用户的密码输入时,您的应用程序将遍历字符串,并假定数据的第1个字节是盐的第1个字节,后跟盐的哈希值。此次遍历将失败。应用程序将继续使用数据的前2个字节作为盐的前2个字节,并重复直到在使用前200个字节作为盐的前200个字节后找到正面结果。如果密码错误,则应用程序将继续尝试所有排列,直到没有找到为止。
此方法的优点:
增加安全性-即使您的身份验证逻辑已知,编译时也不知道确切的逻辑。即使具有确切逻辑的知识,进行暴力攻击也几乎是不可能的。更长的盐长度将进一步增加安全性。
此方法的缺点:
由于在运行时推断出确切的逻辑,因此这种方法非常消耗CPU。盐的长度越长,这种方法就变得越耗费CPU。
验证不正确的密码将涉及最高的CPU成本。这可能会对合法请求产生反作用,但可以增加对攻击者的安全性。
此方法可以以各种方式实现,并且可以通过使用可变宽度的盐和/或盐值密码哈希来使其更加安全。