在为数据库存储散列密码时,我总是使用适当的每项salt字符串。对于我的需要,在DB中将盐存储在散列密码旁边总是很好。

然而,有些人建议将盐与数据库分开存储。他们的论点是,如果数据库被破坏,攻击者仍然可以构建一个彩虹表,将特定的盐字符串考虑在内,以便一次破解一个帐户。如果这个帐户有管理权限,那么他甚至不需要破解任何其他帐户。

从安全的角度来看,把盐存放在不同的地方值得吗?考虑一个web应用程序,其服务器代码和DB位于同一台机器上。如果salt存储在该机器上的平面文件中,那么如果数据库受到威胁,那么盐类文件也会受到威胁。

对此有什么建议的解决方案吗?


彩虹表的重点是,它们是预先创建的,并大量分布,以节省其他人的计算时间——实时生成彩虹表所需的时间与直接破解密码+salt组合所需的时间一样长(因为在生成彩虹表时,实际上所做的是预先运行计算以进行野蛮强制哈希),因此,通过知道盐就可以“生成彩虹表”的说法是虚假的。

在单独的文件中存储salt没有实际意义,只要它们是基于每个用户的——salt的意义只是为了使一个彩虹表不能破坏数据库中的每个密码。


通常,它们被前置到散列中并存储在相同的字段中。

没有必要单独存储它们——关键是为每个密码使用随机盐,这样单个彩虹表就不能用于整个密码哈希集。对于随机盐,攻击者必须单独强行执行每个哈希(或者为所有可能的盐计算一个彩虹表——工作量大得多)。

如果您有一个更安全的存储位置,那么只将哈希存储在那里是有意义的。


我将对此提供一个略有不同的看法。

我总是把盐和咸密码散列混合在一起存储。

例如,我将把盐的前一半放在密码的盐散列之前,把盐的后一半放在密码的盐散列之后。应用程序知道这种设计,因此可以获取该数据,并获得salt和salt -password散列。

我采用这种方法的理由是:

如果密码/散列数据被泄露并落入攻击者之手,攻击者将不会通过查看数据知道盐是什么。通过这种方式,攻击者实际上无法执行暴力攻击来获得与哈希匹配的密码,因为他不知道哈希的起始位置,也无法知道数据的哪些部分是盐的一部分,哪些是盐密码哈希的一部分(除非他知道应用程序的身份验证逻辑)。

如果加盐密码哈希是按原样存储的,那么可以执行暴力攻击来获得一个密码,该密码在加盐和哈希时产生与加盐密码哈希相同的数据。

然而,例如,即使咸化密码哈希按原样存储,但预先挂起了一个随机字节,只要攻击者不知道第一个字节将被丢弃,这也会增加攻击的难度。您的应用程序将知道在用于身份验证用户时丢弃数据的第一个字节。

结论是…

1)永远不要以完全相同的形式存储身份验证应用程序使用的数据。

2)如果可能的话,保持身份验证逻辑的秘密以增加安全性。

更进一步。

如果您不能对应用程序的身份验证逻辑保密,那么很多人都知道您的数据是如何存储在数据库中的。假设您决定将盐腌密码散列与盐腌密码散列混合存储在一起,其中一些盐腌密码散列在盐腌密码散列前面,其余的盐腌密码散列后面。

在生成随机盐时,您还可以随机决定在咸化密码散列之前/之后存储盐的比例。

例如,生成512字节的随机salt。您将salt附加到密码,并获得您的salt密码的SHA-512哈希值。您还生成一个随机整数200。然后存储salt的前200个字节,然后是咸化密码散列,然后是salt的其余部分。

When authenticating a user's password input, your application will pass over the string, and assume the first 1 byte of the data is the first 1 byte of the salt, followed by the salted-hash. This pass will fail. The application will continue by using the first 2 bytes of the data as the first 2 bytes of the salt, and repeat until a positive result is found after using the first 200 bytes as the first 200 bytes of the salt. If the password is wrong, the application will continue to try all permutations until none are found.

这种方法的优点:

增加了安全性——即使您的身份验证逻辑是已知的,但在编译时,确切的逻辑是未知的。几乎不可能执行蛮力攻击,即使知道确切的逻辑。增加盐的长度将进一步提高安全性。

这种方法的缺点:

由于精确的逻辑是在运行时推断出来的,因此这种方法是cpu密集型的。盐的长度越长,这种方法的cpu密集度就越高。

验证错误密码将涉及最高的CPU成本。这可能会对合法请求产生反作用,但会增加针对攻击者的安全性。

这种方法可以通过多种方式实现,并且可以通过使用变宽盐和/或盐密码散列来使其更加安全。


基于ASP的开发。NET MVC 4 Web应用程序的书由William Penberthy:

Getting access to the salts stored in a separate database requires hackers to hack two different databases to get access to the salt and the salted password. Storing them in the same table as the password, or even another table of the same database, would mean that when hackers gain access to the database, they will have access to both the salt and the password hash. Because security includes the process of making hacking into the system too expensive or time-consuming to be worth it, doubling the amount of access a hacker would have to gain should make the system more secure. Ease of use is the primary reason for keeping the salts in the same database as the hashed passwords. You would not have to ensure that two databases are always available at the same time, and always in sync. The advantage of having a salt is minimal if each user has a randomized salt because although it might make discovery of an individual’s password easier, the amount of force necessary to crack the passwords of the system overall will be high. In this level of discussion, that is really what the expectation is: to protect the passwords. If the hackers have acquired a copy of the database, your application data is already compromised. At this point, the issue is to mitigate users’ risks because of the potential of shared passwords. The requirement of maintaining two separate linked, databases is extensive. Granted, it adds the perception of security, but the only advantage that it gives is that it protects a password, a single element of data. If every field in the database were individually encrypted, and this same salt was used for that, it would make more sense to store it separately from the data because the basic security of your system is enhanced.


The point of a salt is to render all rainbow tables useless and require a new set of them to be made. It takes just as long to guess a string as to make a rainbow table. For example the SHA-256 hash of "password" is 5e88 4898 da28 0471 51d0 e56f 8dc6 2927 7360 3d0d 6aab bdd6 2a11 ef72 1d15 42d8. After a salt is added, such as "badpassword" the new string to be hashed is "passwordbadpassword" which, due to the avalanche effect, dramatically changes the output, to 457b f8b5 37f1 802e f9c8 2e46 b8d3 f8b5 721b 7cbb d485 f0bb e523 bfbe 73e6 58d6.

通常情况下,盐只存储在与密码相同的数据库中,这也是因为如果一个数据库被黑客入侵,另一个数据库也很可能会被黑客入侵。


之所以用盐腌是为了防止彩虹表附着。恶意用户以某种方式到达数据库并看到散列密码,获得最常见密码的表,找到它们的散列值并在表中查找密码。

所以当用户发送密码时,我们在密码中添加随机生成的salt。

 userPassword + salt

我们把这个传递给哈希算法。

 hash(userPassword+salt)

因为salt是随机生成的,所以userPassword+salt变成了一个随机值,绝对不是最常用的密码之一。因此恶意用户通过检查彩虹表就不会知道使用了什么密码。

现在salt值被放在哈希值之前,因为在用户登录时再次使用它来比较传递的凭据与保存的凭据。

 hash(userPassword+salt)=ashdjdaskhfjdkhfjdashadslkhfdsdh

这是这个密码如何存储在db: ashdjdaskhfjdkhfjdashadslkhfddhh .salt

现在,如果恶意用户看到这个,他可以找出密码,但这将花费大量的时间。因为每个密码将得到不同的盐。让我们恶意有表5000常用密码和他们的哈希。

重要的一点是,恶意用户不只有一个表。因为有太多不同的算法,所以恶意用户会对每个算法有5000个密码哈希值。

现在,对于每个密码,假设他从第一个用户的密码开始,他将把这个盐添加到5000个常用密码中,并为每个不同的算法创建一个新的彩虹表,以只找到一个密码。然后对于第二个用户的密码,他会看到不同的盐,他会计算新的彩虹表。它甚至不保证,用户的密码将在那些常用密码的列表。


如果您使用的库(或自己创建的库)使用固定大小的字符串作为salt,那么您可以将salt和散列密码存储在同一个字段中。然后拆分存储的值以检索盐和散列密码以验证输入。

使用10个字符的salt和固定的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与散列密码一起存储实际上是无害的。