Peppering (Password Storage)
cheatsheetseries.owasp.org
cheatsheetseries.owasp.org
Likewise, I’m not a fan of encrypting password hashes since there’s no need to have a two-way function for password verification. Again, there’s probably no exploit. But over and over again we’ve seen ways where attackers have been able to exploit systems where a primitive was used for a subtly different problem than the one it was designed for.
Much better: HMAC the password with the key before running it through the hash.
> An alternative approach is to hash the passwords as usual and then encrypt the hashes with a symmetrical encryption key before storing them in the database, with the key acting as the pepper. This avoids some of the issues with the traditional approach to peppering, and it allows for much easier rotation of the pepper if it is believed to be compromised.
It sounds to me that this is the actual recommendation and the other method is more for explaining the traditional approach. Also the two-way function has a clearly stated benefit in this case.
What do you mean by "unlike" in this case? You're storing part of the password locally and getting part of the password from the user. If you don't trust your hashing function enough to do that, why do you trust it for the password in the first place?
Or specifically - are you worried about common prefixes in this case, or something else?
In this case I’m not worried about any specific attacks. Knowing how most password-hashing algorithms are built, it’s probably completely safe to do so. But these tools weren’t necessarily designed with this method of use in mind and haven’t been analyzed extensively with it either, and that should give anyone pause. If they had, they would—like argon2—have a specific input parameter for this purpose.
For what it’s worth, sometimes even like inputs can’t be smashed together safely. For instance HMAC(k, a a| bb) doesn’t uniquely authenticate aa and bb if they’re all or partly user-controlled, since it’s identical to HMAC(key, a | abb) and HMAC(k, aab | b).
The point being, concatenation of inputs to cryptographic functions is often not a safe operation. Triply so when it’s being used to naïvely add a “feature” to a tool that lacks it (e.g., the classic broken example of SHA(key | message) as a poor-man’s HMAC).
The whole idea is to have a factor that is never stored in the database, so a database dump cannot expose hashes that can be attacked. Just as the user may want to change their password periodically, the site operator might want to change their pepper. Without a 2-way function, you can't do that without the user presenting their unencrypted password again.
Now I have to tell her I salt and pepper my passwords.
I don't specialize in security, but I've seen peppers/global-salts in most of the "serious" password storage schemes I've seen. Looks like an easy layer of protection to limit brute forcing hashes from data dumps.
I would like to remind readers that the same applies to "salting and hashing". Developers who are not cryptographers should be using an accepted "key derivation function" and not salting or hashing themselves. Examples of KDFs are PBKDF2, bcrypt and scrypt.
That's not to dismiss discussion of how KDFs should be implemented. That's presumably very on-topic for HN. But this shouldn't be confused with best practice in using a KDF. If you're implementing a KDF in your application instead of using a pre-built one, you're probably doing it wrong.
Concatenating raw inputs is virtually never going to be the solution they’ll give you.
But... bring it to the ultimate conclusion and you essentially are splitting the salt between local and remote storage.
If you get it via an etcd database file from disk or via direct access to the etcd API, then the best approach is usually to have setup the `--encryption-provider-config` option on the API server so that secrets are encrypted with a secret not present on the etcd server.
Let's say you have a secure password hash that includes a work factor, sph(pw, wf) (I'm going to ignore salt to simplify notation).
The most widely used ones such as PBKDF2, bcrypt, and scrypt do not include any good way to upgrade an existing hash to a bigger work factor.
The way that is usually handled is to flag passwords that are hashed with the smaller work factor and the next time the user logs on you can rehash the password with the new work factor.
I don't like that approach. If I'm raising the work factor it is presumably because I decided that the old one is not secure enough. I want to stop using it. I could just invalidate all the old passwords and make everyone go through password recovery, but that is not ideal.
Another approach could be to replace all the password hashes that use the old work factor, sph(pw, old_wf), with sph(sph(pw, old_wf), new_wf) and add a flag indicating that they are double hashed. Then update to sph(pw, new_wf) next time the user logs in.
I still don't like that. It does solve the problem of having insecure hashes while waiting for people to get around to logging in, but it still requires keeping some extra data around (a flag marking double hashed passwords--or worse if we have to deal with people who have gone more than one work factor upgrade without logging in).
What I'd really like is the password hash to be a pair of functions, sph() and sph_cont(), with the following property for integers 0 < wf1 < wf2:
sph(pw, wf2) = sph_cont(sph(pw, wf1), wf1, wf2)
With that, when I upgrade the work factor I can update all the hashed passwords to the new work factor.So...why aren't the common secure password hashes designed this way? Does it introduce some kind of unavoidable security issue?
Just looking at some of them from a computation point of view, not from a security point of view, it seems doable. The general structure is (1) do some setup, (2) do some look wf times, and (3) do some post processing. If the post processing were reversible so that you could get back to the state right after wf iterations then you could do additional iteration to increase the work factor.
Is there something that makes it so that the steps between the final iteration and the output must necessarily be irreversible in order for the whole thing to be secure?
>The salt should be at least 16 characters long.
>The pepper should be at least 32 characters long
16 characters? What characters? digits? alphanumeric? hexadecimal? raw bytes?
I don't see any value of a pepper. It doesn't actually add any protection.
Modern password hashes like argon2 include this feature natively.
This should fail an obvious unit test for the password handling code. The obvious unit tests for this component include a check that minting two password hashes for the same password gives different results.
If people are going to be allowed to meddle with security critical code with no effective oversight you're already dead you just didn't know it yet. They can go modify the password checking code to return true because that seems to "work better" and if nobody rejects that you no longer have password authentication.
If you don't store the username, but only store the hash of username and password you're not exposing usernames if the database is compromised. You have to search every row to see if there is a row with a matching hash, but that seems like a small price to pay.
Then I wondered if you could take it one step farther, and I think you can!
If you store the result of say 15 rounds of hashing in the database to verify things, then you could use the result after only 10 rounds as the basis for an encryption key to decode the rest of that row in the database.
Since hashing is a one-way operation, nobody could take the stored hash and go backward 5 rounds to read anything sensitive in that table without the username and password for each row.
You'd need another table somewhere else with that data not encrypted that way in order to allow password resets.
Wouldn't this expose the pepper to Database attacks (SQL injections, etc), and invalidate the reason for using it in the first place?
If you use a sufficient salt, and a decent hashing algorithm (bcrypt/argon2id), cracking those passwords are impractical for the attacker. If they can do this extremely expensive computational task, an extra pepper reused in all passwords isn't going to add meaningful computational difficulty because the attacker just needs to brute force the secret pepper.
Guarding against this by using a pepper is actually quite a good mitigation. It protects in situations where the attacker has obtained the password DB, but has not achieved access to the application secret (the pepper). Breaches of DB are quite a common attack.
I prefer the approach where they use the pepper to symmetrically encrypt & decrypt the hash, which potentially allows the pepper to be rotated.
Operationally in case of pwd DB being exfiltrated, it's pretty necessary to do something regardless of a strong hash algorithm being used. Pepper, if you can strongly establish it hasn't been breached, can give you a potential option short of resetting everyone's passwords.
Isn't that field level encryption rather than pepper?
For user input like this, you could pass it in as in array that you unnest in a sub-query, and you can then join to that for your pepper for each user ID/password combination which can be passed into the decrypt function.
If an attacker is able to obtain password hashes from two different sources, one of which is storing passwords with bcrypt(sha256($password)) and the other of which is storing them as plain sha256($password), and attacker can use uncracked SHA-256 hashes from the second site as candidate passwords to try and crack the hashes from the first (more secure) site. If passwords are re-used between the two sites, this can effectively allow the attacker to strip off the Bcrypt layer, and to crack the much easier SHA-256 passwords.
OPs solution is neither a salt, nor a pepper. It is however, a nice way to prevent the above problem.