在散列数据库存储密码时,我总是使用正确的每个条目的盐字符串。根据我的需要,将盐存储在哈希密码旁边的数据库中一直运行正常。
但是,有些人建议将盐与数据库分开存储。他们的论点是,如果数据库受到攻击,攻击者仍然可以构建一个彩虹表,将一个特定的盐字符串考虑在内,以便一次破解一个帐户。如果此帐户具有管理员权限,那么他甚至可能不需要破解任何其他帐户。
从安全角度来看,将盐储存在不同的地方是否值得?考虑在同一台机器上具有服务器代码和DB的Web应用程序。如果盐存储在该机器上的平面文件中,则很可能如果数据库受到损害,则salt文件也是如此。
有没有推荐的解决方案?
答案 0 :(得分:243)
彩虹表的重点在于它们是预先创建的并且集中分发以节省其他人的计算时间 - 生成彩虹表需要同样长的时间,因为它只是破解密码+盐组合直接(因为有效地生成彩虹表时正在执行的是预先运行强制哈希的计算),因此通过知道某人可能“生成彩虹表”的参数是虚假的。
将盐存储在一个单独的文件中没有任何意义,只要它们是基于每个用户的 - 盐的目的只是为了使一个彩虹表不能破坏每个用户的密码。 DB。
答案 1 :(得分:33)
我将对此略有不同。
我总是使用salted-password hash存储混合的盐。
例如,我将在密码的salted-hash之前放置salt的前半部分,在密码的salted-hash之后放置salt的后半部分。应用程序知道此设计,因此可以获取此数据,并获取salt和salted-password哈希。
我采用这种方法的理由:
如果密码/哈希数据被泄露并落入攻击者的手中,攻击者将无法知道查看数据的盐是什么。这样,攻击者实际上无法执行暴力攻击来获取与哈希匹配的密码,因为他不知道哈希开头并且无法知道数据的哪些部分是盐的一部分,或者盐渍密码哈希的一部分( ,除非他确实知道您的应用程序的身份验证逻辑 )。
如果salted-password哈希按原样存储,则可以执行暴力攻击以获取密码,该密码在salted和hashed产生与salted-password哈希相同的数据时。
但是,例如,即使salted-password哈希按原样存储,但预先填写了一个随机字节,只要攻击者不知道要丢弃第一个字节,这也会增加攻击难度。在用于验证用户身份时,您的应用程序会知道丢弃数据的第一个字节。
结论......
1)切勿以正确的形式存储您的身份验证应用程序使用的数据。
2)如果可能,请将您的身份验证逻辑保密,以增加安全性。
更进一步..
如果您无法保密应用程序的身份验证逻辑 - 很多人都知道您的数据如何存储在数据库中。并且假设你已经决定将salted-password hash与salt混合在一起,一些盐在salted-password hash之前加上,其余的盐附加在它上面。
生成随机盐时,您还可以随机确定在盐渍密码哈希之前/之后存储的盐的比例。
例如,您生成512字节的随机盐。您将salt附加到您的密码,并获取您的salted密码的SHA-512哈希值。您还生成一个随机整数200.然后存储salt的前200个字节,然后是salted-password哈希,然后是salt的其余部分。
在验证用户的密码输入时,您的应用程序将遍历字符串,并假设数据的前1个字节是salt的前1个字节,后跟salted-hash。这个过程将失败。应用程序将继续使用数据的前2个字节作为salt的前2个字节,并重复直到使用前200个字节作为salt的前200个字节后找到肯定结果。如果密码错误,应用程序将继续尝试所有排列,直到找不到。
这种方法的优点:
提高安全性 - 即使您的身份验证逻辑已知,编译时也不知道确切的逻辑。即使知道确切的逻辑,实际上也不可能进行暴力攻击。增加盐的长度将进一步提高安全性。
这种方法的缺点:
由于在运行时推断出确切的逻辑,因此这种方法非常耗费CPU。盐的长度越长,这种方法就越耗费CPU。
验证不正确的密码将涉及最高的CPU成本。这可能会对合法请求产生反作用,但会增加对攻击者的安全性。
这种方法可以通过各种方式实现,并且可以通过使用可变宽度盐和/或盐渍密码哈希来更加安全。
答案 2 :(得分:22)
通常,它们会被添加到哈希并存储在同一个字段中。
不需要单独存储它们 - 关键是对每个密码使用随机盐,这样就不能对整个密码哈希集使用单个彩虹表。对于随机盐类,攻击者必须分别对每个哈希值进行暴力破解(或者为所有可能的盐计算彩虹表 - 更多的工作)。
如果您有一个更安全的存储位置,那么将哈希存储在那里是有意义的。
答案 3 :(得分:4)
基于开发William Penberthy撰写的ASP.NET MVC 4 Web应用程序书:
答案 4 :(得分:0)
加盐的要点是使所有彩虹表变得无用,并需要制作一组新的彩虹表。 猜测字符串和制作彩虹表所需的时间一样长。
例如,“密码”的SHA-256哈希为5e88 4898 da28 0471 51d0 e56f 8dc6 2927 7360 3d0d 6aab bdd6 2a11 ef72 1d15 42d8
。
添加盐(例如“ badpassword”)后,要哈希的新字符串为“ passwordbadpassword”,由于雪崩效应,该字符串将输出显着更改为457b f8b5 37f1 802e f9c8 2e46 b8d3 f8b5 721b 7cbb d485 f0bb e523 bfbe 73e6 58d6
。
通常,盐仅与密码存储在同一数据库中,这还因为如果一个数据库被黑客入侵,另一个数据库也可能也将被保存。
答案 5 :(得分:0)
使用盐渍的原因是为了防止彩虹表附着。以某种方式进入数据库并查看散列密码的恶意用户获取最常见密码表,找到它们的散列值并在表中查找密码。
所以当用户发送密码时,我们会在密码中添加随机生成的盐。
userPassword + salt
然后我们将其传递给我们的哈希算法。
hash(userPassword+salt)
由于 salt 是随机生成的,userPassword+salt
成为一个随机值,绝对不是最常用的密码之一。所以恶意用户不会通过查看彩虹表来弄清楚使用的密码是什么。
现在将盐值附加到散列值之前,因为它会在用户登录时再次使用,以将传递的凭据与保存的凭据进行比较。
hash(userPassword+salt)=ashdjdaskhfjdkhfjdashadslkhfdsdh
这是密码在数据库中的存储方式:ashdjdaskhfjdkhfjdashadslkhfdsdh.salt
现在如果恶意用户看到这个,他可以找出密码,但需要大量的时间。因为每个密码都会得到不同的盐。让我们恶意拥有 5000 个常用密码及其哈希表。
一件重要的事情,恶意用户不只有一张表。因为不同的算法太多,所以恶意用户每个算法都会有5000个密码的哈希值。
现在对于每个密码,假设他从第一个用户的密码开始,他会将盐添加到 5000 个常用密码中,并为每个不同的算法创建一个新的彩虹表,以仅找到 1 个密码。然后对于第二个用户的密码,他会看到不同的盐,他会计算新的彩虹表。甚至不能保证,用户的密码会在那些常用密码的列表中。
答案 6 :(得分:0)
如果您使用使用固定大小字符串作为 salt 的库(或创建自己的库),那么您可以将 salt 和散列密码存储在同一字段中。然后,您将拆分存储的值以检索盐和散列密码以验证输入。
使用 10 个字符的盐和 40 个字符的固定散列大小,这看起来像这样:
salt = "california"
passwd = "wp8tJ4Pr"
stored_passwd = salt + hash(passwd + salt)
salt = substr(stored_passwd, 0, 10)
hashed_passwd = substr(stored_passwd, 10, 40)
if hash(user_input + salt) == hashed_passwd:
print "password is verified"
因为 salt 的全部目的是防止对预先计算的表(例如彩虹表)进行密码攻击,所以将 salt 与散列密码一起存储实际上是无害的。