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