ChatGPT解决这个技术问题 Extra ChatGPT

Where do you store your salt strings?

I've always used a proper per-entry salt string when hashing passwords for database storage. For my needs, storing the salt in the DB next to the hashed password has always worked fine.

However, some people recommend that the salt be stored separately from the database. Their argument is that if the database is compromised, an attacker can still build a rainbow table taking a particular salt string into account in order to crack one account at a time. If this account has admin privileges, then he may not even need to crack any others.

From a security perspective, is it worth it to store salts in a different place? Consider a web application with the server code and DB on the same machine. If the salts are stored in a flat file on that machine, chances are that if the database is compromised, the salts file will be, too.

Are there any recommended solutions to this?

If there's a place where you can store the salt that the attacker can't get at, then you should just store the passwords there too. But why not use a different salt for every password?
He is using a different salt for every password, jrockway.
How large are your salts? Your salts should be large enough (32 bits?) that there's practically no chance that a rainbow table has been precomputed for it.
@emddudley these days I've been in the habit of using a 64-bit integer as the salt, but there's no reason I can't make them longer.
Author of PWDTK here sourceforge.net/projects/pwdtknet , honestly I wouldn't worry and I would just store salt in the same DB as password. You should always assume salt is known to an attacker anyway so your focus should be on using a LARGE CRYPTO-RANDOM salt and performing enough key stretching (iterations in PBKDF2) so that making even one rainbow table for one known salt is infeasible. Honestly what you are trying to achieve by putting the salt elsewhere is "Security by Obscurity" and generally serves no benefit when you look at things such as another server to potentially go down.

A
Amber

The point of rainbow tables is that they're created in advance and distributed en masse to save calculation time for others - it takes just as long to generate rainbow tables on the fly as it would to just crack the password+salt combination directly (since effectively what's being done when generating rainbow tables is pre-running the calculations for brute-forcing the hash), thus the argument that by knowing the salt someone could "generate a rainbow table" is spurious.

There's no real point in storing salts in a separate file as long as they're on a per-user basis - the point of the salt is simply to make it so that one rainbow table can't break every password in the DB.


Agreed. The threat model you're protecting against by storing the salt separately is a user who can somehow access the salt in the DB through nefarious means, but not the hash (in the DB). And that that person will start computing a rainbow table in advance, assuming he will be able to find the hash later. Not impossible, but also not worth the engineering effort for defending aginst this single attack avenue.
Nice post, I was wondering the same thing. I never thought about a salt per user I was thinking that a single salt would work for all users. What about a salt that is stored as an XML file that is loaded by the App Server? or maybe somehow hardcoded into a servlet?
@Jigzat - Salting is pointless if you don't have a separate salt for each user. The point of salts is to make breaking the hashes a separate task for each user password; if the salt is the same for all of them then that's not the case.
@TomRitter thats not the only case. you assume that all of the passwords are complicated. some attackers may take the salt and hash and check only the 10,000 most common passwords. that way they will get a decent number of people. if, however, they dont have access to the salt, that is akin to the user having a longer more secure password. now, how likely is it that the salt database will stay safe while the password database is stolen is up for debate, but thats a separate issue.
@Amber, I believe TomRitter is correct. Storing the salt separately means the difference between forcing an attacker to use a brute force attack versus an easier dictionary attack. If you know the salt, you can just append it during a run of the mill dictionary attack. If you can 100% defend your salt, you can simply use the same salt and force attackers to brute force everything (even for users who use "password" as their password). But can you defend your salt.... probably not. So might as well reduce points of failure by storing it next to the hash and enforce stronger password rules.
I
Ibraheem

I will provide a slightly different take on this.

I always store the salt mixed in with the salted-password hash.

For example, I will place the first half of the salt before the salted-hash of the password, and the last half of the salt after the salted-hash of the password. The application is aware of this design so can fetch this data, and obtain the salt and salted-password hash.

My rationale for this approach:

If the password/hash data is compromised and falls into the hands of an attacker, the attacker will not know what the salt is from looking at the data. This way an attacker cannot practically perform a brute-force attack to obtain a password that matches the hash, since he doesn't know the hash to begin with and has no way to know which parts of the data are parts of the salt, or parts of the salted-password hash (unless he does know your application's authentication logic).

If the salted-password hash is stored as-is, then a brute-force attack can be performed to obtain a password that when salted and hashed produces the same data as the salted-password hash.

However, for example, even if the salted-password hash was stored as-is, but pre-pended with a single random byte, as long as the attacker is unaware that this first byte is to be discarded, this would also increase the difficulty of attack. Your application would know to discard the first byte of the data when used to authenticate your user.

The conclusion to this..

1) Never store the data that your authentication application uses in it's exact form.

2) If possible, keep your authentication logic secret for added security.

Go one step further..

If you cannot keep your application's authentication logic secret - lots of people know how your data is stored in the database. And suppose you have decided to store the salted-password hash mixed in together with the salt, with some of the salt prepending the salted-password hash, and the rest of the salt appending it.

When generating the random salt, you could also randomly decide what proportion of your salt you will store before/after the salted-password hash.

For example, you generate a random salt of 512 bytes. You append the salt to your password, and obtain the SHA-512 hash of your salted-password. You also generate a random integer 200. You then store the first 200 bytes of the salt, followed by the salted-password hash, followed by the remainder of the 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.

The pros of this approach:

Increased security - even if your authentication logic is known, the exact logic is unknown at compile-time. It is practically impossible to perform a brute-force attack, even with knowledge of the exact logic. Increased lengths of salt will increase security further.

The cons of this approach:

Since the exact logic is inferred at run-time, this approach is very CPU-intensive. The longer the length of the salt, the more CPU-intensive this approach becomes.

Authenticating incorrect passwords will involve the highest CPU cost. This can be counter-productive to legitimate requests, but increases security against attackers.

This approach can be implemented in various ways, and can be made even more secure by using variable-width salts and/or salted-password hashes.


With your approach you are just adding a secret to your hashing process (the algorithm that applies the salt). This secret you can add much easier with adding a pepper additionally to the salt, i tried to point this out in my tutorial. Modern hash functions like BCrypt will apply the salt on their own, using the original salt in each iteration, so you would have no control over this anyway.
@martinstoeckli While you are correct that BCrypt applies the salt on their own, the storage of that salt+hash is up to you as the developer. So, you could easily add a pepper to the salt+hash and persist it to the database. Then, on subsequent retrieval, you read the value from the database, strip the pepper value, and pass the remaining value to BCrypt.
@PeterToTheThird - This would negate the advantage of the pepper. The pepper adds a server side secret, and only works as long as it stays secret (opposite to the salt). A typical attack is SQL-injection, when someone gains access to the database but not to the code, a pepper stored in the database will be useless then. Most BCrypt implementations will add the salt automatically to the resulting hash-value, so this value already contains the salt, the cost factor, the algorithm and the hash. This string can be stored in a single field of 60 characters length.
To add, when using a "key strengthening" function such as BCrypt, you don't have control over usage of the salt. However, if you wanted to use a pepper you'd just append the pepper to the salt, and use that as a "peppered salt" in place of the "salt" input to the hashing function. The "pepper" then is a suitable piece of data that is not stored in the database, but embedded in the authentication code, or stored in another secure location. I approached the problem from a generic perspective, using SHA-512 as an example function, but BCrypt etc. can also be used in a similar way.
@martinstoeckli - yes, the actual implementation depends on which hash function you use. Obviously you need to take the parameters and outputs of the hash function into consideration when implementing your authentication logic. Ultimately, a pepper is just another variable introduced into your hashing function, that is not stored in the same location as the salt and hash.
n
nobody

Often, they are prepended to the hash and stored in the same field.

There is no need to store them separately - the point is to use a random salt for each password so that a single rainbow table can't be used against your entire set of password hashes. With random salts, an attacker must brute-force each hash separately (or compute a rainbow table for all possible salts - vastly more work).

If you had a more secure storage location, it would make sense to just store the hashes there.


But what happens if all the hashed passwords are leaked including their matching salt? Isn't that just as insecure?
@mghaoui But then if you wanted to know the "password" you still would have to construct a Rainbow Table for each and every salt, unless some of the salts are the same.
D
DaNeSh

Based on Developing ASP.NET MVC 4 Web Applications book by 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.


If the application can authenticate to both databases though, isn’t it essentially the same as if it were one database, if the attacker has compromised the application code?
E
Eric Jin

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.

Normally the salt is just stored in the same database as the password, also because if one database is hacked, it is likely that the other will be, also.


Y
Yilmaz

The reason why salting is used to prevent the rainbow table attach. Malicious user, who somehow reached the database and sees the hashed passwords, gets the table of most common passwords, find their hash value and look up the passwords in the table.

So when user sends the password, we add randomly generated salt to the password.

 userPassword + salt

and we pass this to our hashing algorighm.

 hash(userPassword+salt)

since salt is randomly generated, userPassword+salt becomes a random value, defintely not one of the most common used passwords. So malicious user will not figure out the what password used by checking the rainbow table.

Now salt value is prepended to the hashing value, because it is used again when user sign-in to compare the passed credentials with the saved credentials.

 hash(userPassword+salt)=ashdjdaskhfjdkhfjdashadslkhfdsdh

this is how this password stored in db:ashdjdaskhfjdkhfjdashadslkhfdsdh.salt

Now if malicious user sees this, he can figure out the password but it will take tremendous amount of time. Because each password will get a different salt. Let's malicious has table of 5000 common passwords and theirs hash.

One important thing, malicious user does not have only one table. Because there are too many different algorithms, so malicious user will have 5000 password's hash values for each algorithms.

now for each password, let's say he starts with the first user's password, he will add that salt to 5000 common passwords and create a new rainbow table for each different algorithm to find only 1 password. Then for the second user's password, he will see a different salt, he will calculate new rainbow tables. It is not even guaranteed, user's password will be in those common password's list.


e
explogx

If you use a library (or make your own one) which uses a fixed size string as the salt, then you can store both the salt and the hashed password in the same field. You would then split the stored value to retrieve the salt and the hashed password to verify the input.

With a salt of 10 characters and a fixed hash size of 40 characters, this would look like this:

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"

Since the whole purpose of a salt is to prevent password attacks with precomputed tables (e.g. rainbow tables), storing the salt along with the hashed password is actually harmless.