28

First of all, my objective is to avoid storing the salt in the database as plain text. As far as this question is concerned, the salt is not stored in the database.

After discussion in comments and in chat, I've come up with a theory:

It appears that using domain_name + user ID alongside a pepper will provide a sufficient combination of randomness and uniqueness.

Would a method such as this provide just as much security as a random salt without having to store a designated salt in the database as plain text?

12
  • Calling a hash like sha-256 will not generate any entrophy at all. The number of possible inputs will fix the number of possible hash-values. Commented Sep 1, 2013 at 19:09
  • If the input is unknown (like a password), isn't there entropy?
    – user26547
    Commented Sep 1, 2013 at 19:27
  • Ummm I think I asked this already security.stackexchange.com/questions/33860/… Commented Sep 1, 2013 at 20:10
  • 1
    @Griffin - I think the OP's question actually is: "Can storing a salt be avoided completely, derriving the salt from userid, constant text, and the password itself". Commented Sep 1, 2013 at 20:16
  • 1
    See also stackoverflow.com/q/536584/10080. There are some interesting aspects to consider.
    – AviD
    Commented Dec 12, 2013 at 10:27

7 Answers 7

30

The only requirement of a salt is to be globally unique. It doesn't have to be random and it most certainly does not have to be unknown.

Note the keyword globally. A salt must be unique not only in the context of your database, but in the context of every single database out there. An email or an incremental user ID is (very) unlikely to fulfill this requirement. Using a randomly-generated salt does.

EDIT

I'm going to address the whole using a domain + userid as a salt idea that came up in the comments. I do not think this scheme is a good idea especially when compared with just using a random salt.

If your site is a high target one, an attacker might find it worthwhile to generate a rainbow table targeting the admin account before the attack occurs. This might allow him to get access before the attack gets discovered. The domain + userid scheme does not work well when you consider this situation because the admin account is usually within the first few userids in the system especially when userid is incremented using a counter.

That being said, this whole discussion is really moot in practice. Generating random salts is easy and fast. Many password hashing libraries, especially the bcrypt ones already do it for you. There really isn't a point in rolling your own scheme even if it's just as secure. Let's face it, we are all lazy people aren't we? :P Why reinvent the wheel?

15
  • 1
    Technically, using unpredictable salts does provide a minor benefit, since it prevents an attack from starting a brute-force attack before they've compromised your password database. But you're right about (global) uniqueness being much more important. Commented Sep 1, 2013 at 13:13
  • Also, if you don't want to use random salts for some reason, the global uniqueness requirement could be satisfied e.g. by combining a locally unique per-user value (e.g. an e-mail address or a user ID number) with a globally unique system ID (e.g. the domain name of the website). Commented Sep 1, 2013 at 13:16
  • An email would probably fail to provide over-all uniqueness, but what about user ID? What are the chances that a user, across 3 or 4 small websites with 100+ users and 10+ websites with 10,000 users will have the same user ID across any 2 of those websites? I'm not saying that it's impossible, just very unlikely. Also, why is this even a requirement? If a hacker has a user's password, won't they try the existing password before bothering to crack it, regardless of the hashes matching? If the passwords are different, they'll have different hashes anyway.
    – user26547
    Commented Sep 1, 2013 at 17:41
  • 3
    @MisterMelancholy The point of having a globally unique salt is so that the attacker cannot generate a rainbow table before hand.
    – user10211
    Commented Sep 2, 2013 at 0:52
  • 1
    @TerryChia Only where appropriate! ;)
    – Polynomial
    Commented Sep 2, 2013 at 12:43
13

The important feature of a password salt (as Terry Chia correctly notes) is that it should be globally unique: no two password hashes (even on different sites) should ever have the same salt.

Using unique salts protects against various related attack methods that might be employed by an attacker who has compromised the user database and wants to recover the passwords from their hashes:

  • First of all, it prevents an attacker from easily telling if several users have the same password (which would make those users obvious target, since such a common password would likely be easy to guess and provide a high payoff).

  • Conversely, it also prevents an attacker who has compromised multiple sites from being able to easily tell which users have used the same password on both sites.

  • Further, using a unique salt for each user prevents an attacker from obtaining any benefit from attacking multiple accounts at the same time. Without salts (or with non-unique salts), an attacker could hash a guessed password and then compare it against every password hash in the database, making it more likely that they'll obtain at least one match. With unique salts, this is impossible, since the attacker has to choose the salt (and thus a single target user) before hashing each password guess.

  • Finally, unique salts also make it impractical to use precomputed hash look-up tables (like the so-called "rainbow tables") to speed up password cracking, since, essentially, a separate table would be needed for every user.

Basically, the main goal of salting is to ensure that an cracking multiple compromised password hashes together should be no easier than cracking each one of them separately — nothing more and nothing less. Using globally unique salts achieves this goal.


One easy way to ensure global uniqueness (with high likelihood) is to use a sufficiently long random string as the salt. However, other ways exist too.

For example, you could obtain a globally unique salt by combining a locally unique user identifier (such as an e-mail address or an account number) with a fixed, globally unique site identifier (e.g. the domain name of a website), and possibly some other items like a purpose designator (in case you might need more than one unique salt per user) and a timestamp (so than a new password will always get a different salt). For example, the following string would be perfectly good globally unique salt:

"This is a password salt for user 5457 on security.stackexchange.com created at 1 September 2013 13:35:23.94730 UTC"

and so would this one too:

"password_salt:5457:security.stackexchange.com:20130901T133523.94730"

If you'd prefer your salts to be more compact, you could also feed either of the strings above through a cryptographic hash function like SHA-256 and use the output as the salt. Or, if your system provides a built-in GUID generator, you could just use that (assuming that it works as it should, of course).


All that said, there is one potential benefit to making password salts not only unique but also unpredictable: it prevents the attacker from starting a brute-force attack before they've compromised your database.

Basically, if I, as an attacker, knew that your admin account's salt was, say, "12345678", then I could set my computer(s) to compile a lookup table of more or less common passwords hashed with that salt even before I'd found a way to obtain your actual password hash. Once I did find a way to do that (maybe weeks or months later), I could look up the hash in my table, hoping that it might match one of the passwords my program had already tried in the mean time, in which case I would know the password immediately.

However, if I didn't even know what the actual salt you used was, I wouldn't be able to start guessing the password before I first obtained the correct salt. That would take extra time, potentially allowing you to change the password before I managed to crack it.

Again, long random salts are (with high probability) not only unique but also unpredictable. That said, there are other ways to achieve this goal, too. For example, you could take either of the unique example salts shown above and just append some secret value (e.g. a random string stored in a config file, possibly changed regularly) to it. Since this secret would be the same for all users, it wouldn't help with uniqueness, but it would ensure that I wouldn't be able to start a brute-force attack without first somehow learning the secret.


Addendum: Regarding your edits to the question, I'd say that your scheme (using domain name + user ID + pepper as the salt) should be enough. If possible, I'd also include a timestamp, but that obviously requires you to store the timestamp somewhere.

That said, I still find your premise ("the salt is not stored in the database") a bit puzzling. If you can store the password hash itself in the database, why not the salt too?

In fact, many common password hashing schemes don't use a separate database column for the salt at all, they just store it as part of the hash string, e.g. as method$salt$hash, where method identifies the hashing method used, salt and hash are the salt and hash values encoded using some suitable scheme (e.g. base64) and $ is just an arbitrary separator character that doesn't occur in the encoded salt or hash.

8
  • I'm not asking about using a known salt (like domain name + user ID), I'm talking about using salts that contain user-defined values that you can't see without logging into the account in the first place (like email, or user password itself). This prevents pre-computed rainbow tables for specific users, and keeps me from having a salt field in a database table. Wouldn't that be beneficial because a hacker couldn't immediately hashing if the database was compromised?
    – user26547
    Commented Sep 1, 2013 at 17:48
  • Using the password as (part of) the salt doesn't help anything. Think about it a bit and you'll see why. (Hint: How is the salt used in password hashing?) Using some user-specified value that the attacker cannot guess could indeed help make the salt unpredictable, but things like e-mail addresses can often be pretty easy to guess -- people tends to post them all over the web. You really should just store the salt in the database, either in a separate column or (as is common) just prepended to the password hash. Commented Sep 1, 2013 at 18:25
  • Using a password as part of a salt looks perfectly viable to me: It'd be "password + password". Alone, it'd be a bad salt because it's not unique, but when doing something like "user ID + - + password + password" would be unique and unknowable, correct? Note that the hyphen is needed for cases where user ID 1 uses the password 23, and user ID 12 uses the password 3.
    – user26547
    Commented Sep 1, 2013 at 18:45
  • 2
    @MisterMelancholy: Why do you think an attacker would have any more difficulty creating a lookup table for userID + password + password than for just userID + password (with or without delimiters)? Commented Sep 1, 2013 at 19:01
  • @MisterMelancholy - To double the password would help nothing at all, this would be just another hash algorithm. It would not even add a server side secret (the algorithm), since the salt is normally stored plaintext. You could then build 1 rainbowtable to recover all passwords of your database. Commented Sep 1, 2013 at 19:01
6

A salt is supposed to be unique, must be long enough, and should be unpredictable. Randomness is not necessary, but it is the easiest way for a computer to meet those requirements. And it is not the purpose of a salt to be secret, a salt fulfills its purpose even when known.

Uniqueness means that it should not only be unique in your database (otherwise you could use a userid), it should be unique worldwide. Somebody could create rainbowtables for salts like e.g. 1-1000 and would be able to retrieve passwords for all accounts with those userids (often admin accounts have low userids).

Long enough: If the salt is too short (too few possible combinations), it becomes profitable again to build rainbow-tables. Salt and password together can then be seen as just a longer password, and if you can build a rainbow-table for this longer passwords, you also get the shorter original passwords. For very strong and long passwords, salting would actually not be necessary at all, but most human generated passwords can be brute-forced because they are short (people have to remember them).

Also using salts derrived from other parameters can fall into this category. Only because you calculate a hash from the userid, this doesn't increase the possible combinations.

Unpredictability is a bit less important, but imagine once more the case that you use the userid as salt, an attacker can find out what the next few userids will be, and can therefore precalculate a narrow number of rainbow-tables. Depending of the used hash-algorithm this can be applicable or not. He has a time advantage then, can retrieve the password immediately. More of a problem will be, if the admin accounts used a predictable salt.

So using a really random number, generated from the OS random source (dev/urandom), is the best you can do. Even when you ignore all reasons above, why should you use a derrived salt when there is a better way, why not use the best way you know?

UPDATE: to answer the changed question:

Using only domain_name + user_id to generate the salt is surely not a good idea, because it can be guessed and makes it predictable. A pepper won't help here, because it is as constant as the domain_name, you should not rely on it to be secret. With the user_id you gain uniqueness, with the domain_name you get global uniqueness, but you are missing the unpredictable part.

In your previous example you added the password itself as random parameter like salt = hash(domain_name + user_id + password). This could work as long as the salt won't be stored at all, to verify the password you would have all necessary parameters. I cannot see a flaw in this concept, but this doesn't mean that i recommend it. As already mentioned, you cannot do better than generating a random independend salt. Adding this salt to the hash and extracting it for verification is not a hard job, most appropriate hash functions will already do it for you, because they also have to store the cost factor in the same way.

14
  • Technically, as long as each salt is globally unique, their length doesn't matter. (And conversely, just making your salts longer doesn't help if they're not unique.) Salting doesn't really help much against brute-force attacks targeted against a single user (as the middle part of your answer would seem to suggest); for that, you want key stretching. Commented Sep 1, 2013 at 13:22
  • @IlmariKaronen - Just wrote a bit more clear what i meant (about the middle part), probably you didn't notice the edit. And of course key-stretching is a must today. Commented Sep 1, 2013 at 13:24
  • Salting with user ID would be unique for a single database. The chances of the same user having the same ID across 2 (or 3 or 4) websites is likely very low. If a hacker pre-computes a rainbow table with IDs 1-1000, the hash is vulnerable because you used a predictable salt, and is why I stated in my question that salting with the user ID alone is not a good idea.
    – user26547
    Commented Sep 1, 2013 at 17:31
  • 1
    @MisterMelancholy - It doesn't matter, whether a user has the same id on another site, a rainbow-table with those salts would contain "all" passwords with all of the choosen salts, thus working for every site. About the email, you cannot presume that the emails are hidden from the public, hashes are made or the case, when the database is already stolen, so the email addresses will be known as well. Using the email as salt, maybe combined with other data and hashed, is not the worst thing you can do, but using a random independend salt is always better. Commented Sep 1, 2013 at 18:52
  • @MisterMelancholy - You already answered it well. The hash is used to make it impossible to retrieve the original password, so storing the password as salt in a retrievable form will undo the whole effort (you need it for verification). If you want to add a server side secret, a good method is, to encrypt the password-hash with a server side key, or to use a pepper. But don't mix up the two, let the salt be known (plaintext) and the server-side key be a secret. Commented Sep 1, 2013 at 19:21
4

First of all, my motive is to avoid storing the salt in the database as plain text.

Why? There is no good reason to avoid storing the salt in the database in plain text. This is how a salt is normally stored.

With the various iterations of your question, you've proposed various ways to do things differently, but they all either end up having the same security properties, or (usually) make things worse.

The point of a salt is to prevent attackers from sharing the work when cracking many accounts at once. Often attackers are not after a particular user on a particular site, what they want is to get a foothold or to crack as many accounts as possible. The point of salt is to make cracking Joe's account on the ACME site require completely different computations from cracking Jane's account or Joe's account on the Yoyodyne site. Hence the need for a salt to be globally unique: across accounts, and across databases (and even across time).

If you use the password as part of the salt calculation, it isn't a salt at all. The salt needs to be independent from the password. If you calculate the salt from the password, then it becomes part of the hash function: H(S(P),P) is a function of P, you've only created a new hash function H'(P) = H(S(P),P).

Using the user name is not good because it can be the same across databases. An attacker with the ACME database and the Yoyodyne database can crack both databases without duplicating the effort if they use salt that is derived from the user name.

If you use the domain name and the user ID, that can work. Make sure that no one else in the world who uses the same domain name; in particular, don't use the same domain name if you have more than one subsite: include the service name or whatever it takes to make the name unique. Also, don't reuse user IDs. As long as your salt is globally unique, it's a proper salt.

Note however that you will not gain any security from using the domain name and user ID as the salt rather than a random salt as is typically done. The salt is still effectively stored in the database, just in some weird way. You save a few bytes per database record, which shouldn't be significant.

A pepper does not significantly improve security either. You cannot assume that your pepper remains secret. If the attacker has been able to obtain a copy of your password database (typically by compromising your application or by gaining access to a backup), it's likely that they also have access to the pepper (it needs to be accessible to the application and backed up, after all). The only case where a pepper is useful is a compromise such as SQL injection which only grants the attacker access to the database and does not extend to an application compromise, not even via an administrative account. Don't count on it. Even if you use a pepper, your salt must be globally unique.

I strongly recommend generating a random salt and storing it alongside the password. Preferably, don't code it up yourself: use an existing library that does it right. The more you deviate from recommended practice, the more you risk doing it wrong — perhaps completely, dangerously wrong like deriving the salt from the password.

Remember that in addition to be salted, password hashes must be slow. That means PBKDF2 or bcrypt or scrypt, with an iteration count adjusted for current computer speeds.

For more background, read our numerous threads on this subject:

5
  • "you will not gain any security from using the domain name and user ID as the salt". Isn't the point of the salt to "to prevent attackers from sharing the work when cracking many accounts at once"? The salt alone isn't supposed to increase security; it's so that a hacker can't re-use their work for one hash on another. And "You cannot assume that your pepper remains secret." I find this argument kind of a moot point. If a hacker gets server access and finds your password hashing function, aren't you equally screwed whether you're using bcrypt or something else? with or without a pepper?
    – user26547
    Commented Sep 4, 2013 at 8:26
  • 1
    @MisterMelancholy I meant in comparison with a random salt, not in comparison with no salt at all. Domain name + user ID is roughly as secure as a random salt, if you do it right (but has no advantage). The attacker can get the application code without getting access (e.g. from a compromised backup), it's a fairly common scenario that you should protect against. Furthermore you should protect the passwords themselves even if the attacker gains access to the accounts, because the passwords may be reused elsewhere. Commented Sep 4, 2013 at 8:30
  • I think it has an advantage; it means one less field in my database as well as not having a designated "salt" field. Without source code, the hacker is even more lost than they'd be with a salt field. Also, "The attacker can get the application code without getting access (e.g. from a compromised backup)" Isn't this just as likely to hold the password hashing function as it is to hold the pepper?
    – user26547
    Commented Sep 4, 2013 at 8:37
  • @MisterMelancholy The salt is usually stored together with the hash, this is standard practice, but you can have a separate salt field (and iteration count or analogous work factor value, that needs to be stored as well). The password hashing function doesn't need to be secret. Never assume that the hacker doesn't have the source code: if your security depends on that, you've already lost. This is a well-known security principle. Commented Sep 4, 2013 at 9:13
  • 1
    @MisterMelancholy - You use a slow hash like BCrypt and a pepper, exactly for the case a hacker gets access to your database or server. If the hash function is slow, an attacker can calculate e.g. only 1 hash instead of 8 Giga hashes. The pepper requires the attacker to have privileges on the server, what is more difficult than just having read access to the database. Commented Sep 4, 2013 at 11:32
3

Since none of the other answers seem to touch on this:

DO NOT use part of the password as the salt. In addition to decreasing unpredictability, it will, when an attacker figures it out, help an attacker break the passwords. Why? Because they have part of it right there, next to the password in the database.

Update: If you are using part of the password in the salt, and you want to avoid having to store the salt, then pulling part of the password after it's entered to calculate the salt will work well. It further complicates brute forcing, and it's just as effective as a regular salt at increasing entropy. It makes rainbow tables a bit harder to make, as you need to calculate the salt per password, but it doesn't really prevent them any better than a regular salt.

7
  • "First of all, my motive is to avoid storing the salt in the database as plain text. I'd like to avoid "that's bad because the salt is stored in plain text" kind of responses."
    – user26547
    Commented Sep 1, 2013 at 20:30
  • 2
    @MisterMelancholy Yet this is the most important point. If you don't store the salt, it becomes part of the hash function, thus is no salt at all. If you do store the salt, you need to protect it, since it contains the hash — and you'll need a way to retrieve the salt, which will be equally usable by an attacker. Commented Sep 1, 2013 at 20:32
  • But the hash generated for the salt won't be stored in the database. That's the whole point of this question. There will only be one hash stored in the database. The only way they could find the hash for your salt is to crack the original password hash, thus making it irrelevant that the salt hash holds the password because they'll have the password by the time they get the hash. The salt hash will be generated on-the-fly why whatever server langauge, and should be used for nothing more than for entropy and length to your salt. @demize
    – user26547
    Commented Sep 1, 2013 at 20:41
  • It is not stored. The reliable way of re-generating it is using the password that's being hashed in the first place. Also, the only reason I've ever heard "a salt does not have to be protected" is because of stupid policies I don't agree with (hence this question). A salt is just a recyclable and unique pepper, which is stupid because this method could provide every possible purpose of a salt and a pepper combined if it's treated as a salt and pepper combined. Also, it's true that this opens another attack vector, however, you must pass the first attack vector to get to it. @demize
    – user26547
    Commented Sep 1, 2013 at 20:53
  • @demize - Well that's the actual question, does it really introduce a new attack vector and how? The userid would make the salt unique, the domainname would make it globally unique, and the password would make it unpredictable. It is possible to regenerate it, and when the password changes, it would result in a different salt. Commented Sep 1, 2013 at 20:55
1

In addition to @terry-chia's outstanding answer there is another reason to pick random salts. The hashing and authentication scheme that you are using today may place relatively weak requirements on salting, but someday you might (or a successor) might modify the scheme used in a way that does have a randomness requirement.

So although randomness isn't a requirement, you are far safer if you do pick random salts, as that will guarantee that you get all of the things required for your scheme today, but it will also provide you with what you may need tomorrow.

Again, an additional, reason on top of what others have already explained. Do not derive the salt from user data! Using a random salt may get you more than you need, but it will ensure that you don't make other errors. You have nothing to gain in not using a random salt (unless there is something you haven't explained yet), while you make your system far less robust by just going for the minimal requirements of a salt.

1
  • I've updated my question a number of times (and am done), and though it's asking the same basic principle, I'm now asking each person with an answer to consider revising their answer to make sure it's still on-topic to the question. Sorry for my indecisiveness, and thank you for your time and input.
    – user26547
    Commented Sep 4, 2013 at 6:52
-3

Any salt whatsoever improves your security. Without a salt, an attacker could identify all 1,000 accounts with password 123456. They wouldn’t know the password but they would know it’s very, very common and most likely easy to crack. So that’s a bullseye. If these 1000 accounts all use different salts except for two equal ones, then 998 give no clue to the password strength and will not be targeted. Two give a clue, but cracking them gives the attacker only two passwords, not 1000.

If you create a long, random salt then it is very unlikely to be used by someone else. And if your password is secure then it is also highly unlikely that someone has the same password, and double unlikely that both have the same hash value.

And if two users have the same hash by pure coincidence (different salts, different strong passwords) that is the worst case for the attacker. Because they think the password is weak and try to crack it but fail and waste time because they are both strong.

2
  • It seems absurd to think that attackers will give up just because they cannot immediately see all accounts with the password "123456" due to salts. Nothing prevents them from simply trying this password (and other common choices) on all hashes. This is extremely cheap computationally, even in the case of modern password hashing algorithms like Argon2. So an attacker will most definitely find the accounts with very weak passwords, regardless of whether or not salts are used.
    – Ja1024
    Commented May 15 at 22:49
  • The purpose of salts is not to hide weak passwords or to mask the fact that multiple accounts use the same password. An attacker can easily find this out by simply trying common password choices or checking whether a found password also works for other hashes. Salts prevent an attacker from reusing hash calculations across accounts. If they want to try m passwords on n accounts, they have to do m * n calculations. Without salts, they would only need m calculations whose results can be compared against all hashes. Salts also prevent precomputed hash tables, but this is a secondary effect.
    – Ja1024
    Commented May 15 at 22:55

You must log in to answer this question.