把盐藏起来做大杂烩的必要性

关于盐,我们有两种相互矛盾的理论。我开发的产品使用类似于用户名或电话号码的东西来加盐散列。从本质上说,这些对于每个用户都是不同的,但是对于我们来说是可用的。另一个产品随机为每个用户生成一个 salt,并在用户每次更改密码时进行更改。然后在数据库中对 salt 进行加密。

我的问题是,第二种方法是否真的有必要?我可以从纯理论的角度来理解,它比第一种方法更安全,但从实用性的角度来看呢。现在,要对用户进行身份验证,必须解密 salt 并将其应用于登录信息。

仔细考虑之后,我并没有从这种方法中看到真正的安全收益。即使攻击者知道如何快速确定每个帐户的结果,将 salt 从一个帐户更改为另一个帐户,仍然会使某人极难强制执行散列算法。这是在假设密码足够强的前提下进行的。(显然,找到一组密码的正确哈希值,如果它们都是两位数字,要比找到正确的哈希值(8位数字)容易得多)。是我的逻辑错了,还是我漏掉了什么?

编辑: 好的,这就是为什么我认为加密 salt 毫无意义的原因。(让我知道我是否在正确的轨道上)。

对于下面的解释,我们假设密码总是8个字符,salt 是5,并且所有的密码都由小写字母组成(这使得计算更容易)。

对每个条目使用不同的盐意味着我不能使用相同的彩虹表(实际上,如果我有一个足够大的,我可以使用彩虹表,但让我们暂时忽略它)。根据我的理解,这才是真正的关键,因为要破解每一个账户,我必须重新发明轮子,这样才能代表每一个账户。现在,如果我知道如何将正确的 salt 应用于密码以生成散列,我会这样做,因为 salt 实际上只是扩展了散列短语的长度/复杂度。所以我需要减少可能需要生成的组合的数量来“知道”密码 + salt 从13 ^ 26减少到8 ^ 26因为我知道 salt 是什么。这样就简单多了,但还是很难。

所以要加密盐。如果我知道 salt 是加密的,我就不会首先尝试解密它(假设我知道它有足够级别的加密)。我会无视它。我没有尝试解密它,而是回到前面的例子,我只是生成了一个更大的彩虹表,其中包含了13 ^ 26的所有密钥。不知道盐的存在肯定会拖慢我的速度,但我不认为这会增加首先破解盐密码的艰巨任务。所以我觉得不值得。有什么想法吗?

下面的链接描述了密码在穷举法下能保存多长时间: Http://www.lockdown.co.uk/?pg=combi

30286 次浏览

My understanding of "salt" is that it makes cracking more difficult, but it doesn't try to hide the extra data. If you are trying to get more security by making the salt "secret", then you really just want more bits in your encryption keys.

There are two techniques, with different goals:

  • The "salt" is used to make two otherwise equal passwords encrypt differently. This way, an intruder can't efficiently use a dictionary attack against a whole list of encrypted passwords.

  • The (shared) "secret" is added before hashing a message, so that an intruder can't create his own messages and have them accepted.

The answer here is to ask yourself what you're really trying to protect from? If someone has access to your database, then they have access to the encrypted salts, and they probably have access to your code as well. With all that could they decrypt the encrypted salts? If so then the encryption is pretty much useless anyway. The salt really is there to make it so it isn't possible to form a rainbow table to crack your entire password database in one go if it gets broken into. From that point of view, so long as each salt is unique there is no difference, a brute force attack would be required with your salts or the encrypted salts for each password individually.

The second approach is only slightly more secure. Salts protect users from dictionary attacks and rainbow table attacks. They make it harder for an ambitious attacker to compromise your entire system, but are still vulnerable to attacks that are focused on one user of your system. If you use information that's publicly available, like a telephone number, and the attacker becomes aware of this, then you've saved them a step in their attack. Of course the question is moot if the attacker gets your whole database, salts and all.

EDIT: After re-reading over this answer and some of the comments, it occurs to me that some of the confusion may be due to the fact that I'm only comparing the two very specific cases presented in the question: random salt vs. non-random salt. The question of using a telephone number as a salt is moot if the attacker gets your whole database, not the question of using a salt at all.

Here is a simple example showing why it is bad to have the same salt for each hash

Consider the following table

UserId  UserName,   Password
1  Fred       Hash1 =  Sha(Salt1+Password1)
2  Ted        Hash2 =  Sha(Salt2+Password2)

Case 1 when salt 1 is the same as salt2 If Hash2 is replaced with Hash1 then user 2 could logon with user 1 password

Case 2 when salt 1 not the same salt2 If Hash2 is replaced with Hash1 then user2 can not logon with users 1 password.

Really, it depends on from what type of attack you're trying to protect your data.

The purpose of a unique salt for each password is to prevent a dictionary attack against the entire password database.

Encrypting the unique salt for each password would make it more difficult to crack an individual password, yes, but you must weigh whether there's really much of a benefit. If the attacker, by brute force, finds that this string:

Marianne2ae85fb5d

hashes to a hash stored in the DB, is it really that hard to figure out what which part is the pass and which part is the salt?

Hiding a salt is unnecessary.

A different salt should be used for every hash. In practice, this is easy to achieve by getting 8 or more bytes from cryptographic quality random number generator.

From a previous answer of mine:

Salt helps to thwart pre-computed dictionary attacks.

Suppose an attacker has a list of likely passwords. He can hash each and compare it to the hash of his victim's password, and see if it matches. If the list is large, this could take a long time. He doesn't want spend that much time on his next target, so he records the result in a "dictionary" where a hash points to its corresponding input. If the list of passwords is very, very long, he can use techniques like a Rainbow Table to save some space.

However, suppose his next target salted their password. Even if the attacker knows what the salt is, his precomputed table is worthless—the salt changes the hash resulting from each password. He has to re-hash all of the passwords in his list, affixing the target's salt to the input. Every different salt requires a different dictionary, and if enough salts are used, the attacker won't have room to store dictionaries for them all. Trading space to save time is no longer an option; the attacker must fall back to hashing each password in his list for each target he wants to attack.

So, it's not necessary to keep the salt secret. Ensuring that the attacker doesn't have a pre-computed dictionary corresponding to that particular salt is sufficient.


After thinking about this a bit more, I've realized that fooling yourself into thinking the salt can be hidden is dangerous. It's much better to assume the salt cannot be hidden, and design the system to be safe in spite of that. I provide a more detailed explanation in another answer.


However, recent recommendations from NIST encourage the use of an additional, secret "salt" (I've seen others call this additional secret "pepper"). One additional iteration of the key derivation can be performed using this secret as a salt. Rather than increasing strength against a pre-computed lookup attack, this round protects against password guessing, much like the large number of iterations in a good key derivation function. This secret serves no purpose if stored with the hashed password; it must be managed as a secret, and that could be difficult in a large user database.

... something like a user name or phone number to salt the hash. ...

My question is if the second approach is really necessary? I can understand from a purely theoretical perspective that it is more secure than the first approach, but what about from a practicality point of view?

From a practical point of view, a salt is an implementation detail. If you ever change how user info is collected or maintained – and both user names and phone numbers sometimes change, to use your exact examples – then you may have compromised your security. Do you want such an outward-facing change to have much deeper security concerns?

Does stopping the requirement that each account have a phone number need to involve a complete security review to make sure you haven't opened up those accounts to a security compromise?

A hidden salt is no longer salt. It's pepper. It has its use. It's different from salt.

Pepper is a secret key added to the password + salt which makes the hash into an HMAC (Hash Based Message Authentication Code). A hacker with access to the hash output and the salt can theoretically brute force guess an input which will generate the hash (and therefore pass validation in the password textbox). By adding pepper you increase the problem space in a cryptographically random way, rendering the problem intractable without serious hardware.

For more information on pepper, check here.

See also hmac.

I tend to hide the salt. I use 10 bits of salt by prepending a random number from 1 to 1024 to the beginning of the password before hashing it. When comparing the password the user entered with the hash, I loop from 1 to 1024 and try every possible value of salt until I find the match. This takes less than 1/10 of a second. I got the idea to do it this way from the PHP password_hash and password_verify. In my example, the "cost" is 10 for 10 bits of salt. Or from what another user said, hidden "salt" is called "pepper". The salt is not encrypted in the database. It's brute forced out. It would make the rainbow table necessary to reverse the hash 1000 times larger. I use sha256 because it's fast, but still considered secure.