Re:Peppering (Password Storage)
by stouset on Thursday July 02, 2020 @03:32AM
(#23710826)
I don’t like the OWASP recommendation here to concatenate the pepper and password before having. Generally speaking, you should not be smashing unlike things together into a single cryptographic input. If you find yourself doing this sort of thing, it’s worth taking a step back and seeing if there’s another primitive that better meets your needs. It’s probably fine to do it this way for anything approaching a reasonable password hashing function, but it does leave a bad taste in one’s mouth.
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.
Re:Peppering (Password Storage)
by ascar on Thursday July 02, 2020 @03:45AM
(#23710898)
The article directly proposes an alternative that "avoids some of the issues" with adding the pepper before hashing:
> 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.
Re:Peppering (Password Storage)
by linsomniac on Thursday July 02, 2020 @11:05AM
(#23713997)
The article explains why you want a 2-way function: it allows you to rotate the pepper. If you have a one-way pepper, you can't change the pepper if it were to be exposed.
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.
Re:Peppering (Password Storage)
by viraptor on Thursday July 02, 2020 @03:53AM
(#23710933)
> smashing unlike things together into a single cryptographic input
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?
Re:Peppering (Password Storage)
by stouset on Thursday July 02, 2020 @11:59AM
(#23714705)
The “pepper” should be a cryptographically random key. A password is nothing even close to that.
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).
Re:Peppering (Password Storage)
by Waterluvian on Thursday July 02, 2020 @10:33AM
(#23713530)
My wife went on a light hearted rant last night about how us engineers steal all the normal words. “Sometimes it almost sounds like English. Just enough to trick my brain into trying to parse it. Then it hurts.”
Now I have to tell her I salt and pepper my passwords.
Re:Peppering (Password Storage)
by lawnchair_larry on Thursday July 02, 2020 @04:17AM
(#23711071)
For those who don’t specialize in security, this is not an accepted practice. OWASP is a wiki that anyone with an idea can edit, and there are some bad ones on there.
Re:Peppering (Password Storage)
by Jwarder on Thursday July 02, 2020 @11:46AM
(#23714565)
What's wrong with it?
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.
Re:Peppering (Password Storage)
by rlpb on Thursday July 02, 2020 @10:11AM
(#23713266)
This article says: "Writing custom cryptographic code such as a hashing algorithm is really hard and should never be done outside of an academic exercise."
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.
Re:Peppering (Password Storage)
by stouset on Thursday July 02, 2020 @12:13PM
(#23714835)
Long story short, if you find yourself adding security features (for instance, peppers) to cryptographic tools that don’t accept them as clearly-labeled inputs, talk to a cryptographer first.
Concatenating raw inputs is virtually never going to be the solution they’ll give you.
Re:Peppering (Password Storage)
by noodlesUK on Thursday July 02, 2020 @02:31AM
(#23710570)
This is a very good idea if you know you will be on a platform that supports long-lived secrets like k8s or a provider like AWS. It means a db dump is way less dangerous in terms of password reuse, assuming the compromise was something like SQLi.
Re:Peppering (Password Storage)
by sroussey on Thursday July 02, 2020 @04:03AM
(#23710988)
Yeah, but the reality is somewhat more complicated when you operationalize. The pepperId can be the same for everything, or can be per shard, or just chunked across large sets of records. And pepperId = 1 can have the value of the empty string, which is what all non-peppered have. Now it’s easy to increment the pepperId for new records and have a real value stored elsewhere.
But... bring it to the ultimate conclusion and you essentially are splitting the salt between local and remote storage.
Re:Peppering (Password Storage)
by raesene9 on Thursday July 02, 2020 @06:42AM
(#23711839)
if you're on k8s, and you get access to secret objects via the Kubernetes API, you're getting the secrets in the clear, no hashing or encryption.
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.
Re:Peppering (Password Storage)
by tzs on Thursday July 02, 2020 @12:06PM
(#23714759)
A question about upgrading work factors.
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?
Re:Peppering (Password Storage)
by Tostino on Thursday July 02, 2020 @03:07PM
(#23716804)
I've had the same question for a few years. Never bothered to ask or research though oh, so I'm glad to see that you asked and I'm eager to see what the consensus is.
Re:Peppering (Password Storage)
by GoblinSlayer on Friday July 03, 2020 @08:54AM
(#23723528)
It will have a performance tradeoff that you can spend on more iterations. The concern of extra data seems trivial, the output of bcrypt is already a serialized structure and should be easy to extend, and technically is not different from planned hash migration.
Re:Peppering (Password Storage)
by Puts on Thursday July 02, 2020 @08:00AM
(#23712204)
I think the use of stored procedures is underrated. Create a stored procedure to verify the password and only grant permission for the web application-user to run this procedure, nothing more. No one but the database itself needs, or can ever access the hash-column this way. This way you still keep the application separate from the data. I would even argue that we need to get back to the time where there was a DBA handling the data. The dev-ops thinking of mixing application logic and data has introduced a lot of unnecessary security issues.
Re:Peppering (Password Storage)
by jwlake on Thursday July 02, 2020 @10:11AM
(#23713267)
I don't know if this means I'm old but normal people used to just call this a global salt. The pepper term seems a little cute and not nearly as obvious as "global salt". Though it did get me to click it, which I guess means it market tests well.
Re:Peppering (Password Storage)
by willcipriano on Thursday July 02, 2020 @06:46AM
(#23711861)
I'm a big fan of peppering. It strikes me as a defense in depth move. Say a programmer on your team accidentally pushes code that makes your salt a static value for all users going forward, the pepper might protect user passwords in that case.
Re:Peppering (Password Storage)
by tialaramex on Thursday July 02, 2020 @09:34AM
(#23712905)
> Say a programmer on your team accidentally pushes code that makes your salt a static value for all users going forward
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.
Re:Peppering (Password Storage)
by willcipriano on Thursday July 02, 2020 @12:50PM
(#23715209)
You've never seen someone break or add a @ignore to a unit test they don't understand in order to push out some code? Unfortunately I have, either way that isn't the only situation where a pepper is useful.
Re:Peppering (Password Storage)
by tialaramex on Thursday July 02, 2020 @01:35PM
(#23715781)
I've never reviewed such a change. If I saw it I guess it would be one of the rare cases where rather than either writing LGTM or asking some questions and giving approval conditional on getting acceptable answers (or revised code that obviates the questions), I would just press "Reject".
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.
Re:Peppering (Password Storage)
by willcipriano on Thursday July 02, 2020 @02:34PM
(#23716459)
I've never reviewed such a change but I've fixed bugs where a test that would've caught the bug was ignored because it caused the build pipeline to fail. My guess the programmer was on a tight deadline and had to ship it so they cut a corner and forgot to get back to it. That's the reality of many if not most organizations, good software engineering is a afterthought compared to the concerns of keeping the lights on day to day. As such I have changed my personal operational model and include as many cheap safeguards as I can when I'm doing something important. This is just one of those things I have in my anti-footgun bag of tricks.
Re:Peppering (Password Storage)
by willcipriano on Thursday July 02, 2020 @07:12AM
(#23711978)
No, because the static salt will be stored in the database alongside the passwords. The pepper would be compiled and live in the application layer. The value is minimal but so is the cost.
Re:Peppering (Password Storage)
by Sherl on Thursday July 02, 2020 @08:23PM
(#23719643)
If it's static salt, it breaks the use of salt.
Salt was designed such that a rainbow table cannot crack two similar passwords based on hash, exactly what a static salt would facilitate.
Re:Peppering (Password Storage)
by stouset on Thursday July 02, 2020 @12:17PM
(#23714874)
If not stored in the database, it ensures that a database dump alone isn’t enough to recover even a single password. It certainly doesn’t have the level of impact that salting passwords or using a cpu-hard and memory-hard hash does, but it’s certainly something.
Modern password hashes like argon2 include this feature natively.
Re:Peppering (Password Storage)
by pontifier on Saturday July 04, 2020 @01:47AM
(#23730421)
I was working on a password storage system, and was thinking about how I hate storing user info in the database. I was thinking that you might be able to change the way the username and password are stored on a production database.
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.
Re:Peppering (Password Storage)
by Ayesh on Thursday July 02, 2020 @04:22AM
(#23711095)
The article is saying it's just an option. Don't bother.
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.
Re:Peppering (Password Storage)
by twhitmore on Thursday July 02, 2020 @05:05AM
(#23711351)
Exfiltration of password databases, for offline hash cracking, is a well known attack.
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.
Re:Peppering (Password Storage)
by dylz on Thursday July 02, 2020 @05:56AM
(#23711603)
It does functionally result in not being able to extract the entirety of the password from the database though (think SQLI SELECT back), correct me if I'm wrong
Re:Peppering (Password Storage)
by Tostino on Thursday July 02, 2020 @03:15PM
(#23716888)
That depends, you can symmetrically encrypt and decrypt in postgres using functions.
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.
Re:Peppering (Password Storage)
by heinrich5991 on Thursday July 02, 2020 @04:42AM
(#23711219)
Brute-forcing the pepper is computationally infeasible if it was generated with enough entropy. This means the passwords are safe unless both the DB and the pepper is leaked. Even if the user chooses the passowrd "123", the attacker cannot see this with purely the DB. A decent hashing algorithm doesn't help in that way.
Re:Peppering (Password Storage)
by stouset on Thursday July 02, 2020 @12:25PM
(#23714953)
To prevent hash reuse, you can just use a long,
cryptographically-random salt. There exist zero reused bcrypt, scrypt, and argon2 hashes globally (assuming “good” implementations that generate salts on users’ behalf).
Re:Peppering (Password Storage)
by rocqua on Thursday July 02, 2020 @07:09AM
(#23711961)
Indeed it is not a salt, but it is not what OP claimed. Instead, I think OP was providing a good solution to the following problem stated in the article about using a hash to overcome password length limitations:
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.
Re:Peppering (Password Storage)
by coding123 on Thursday July 02, 2020 @11:27AM
(#23714341)
um, no. If using bcrypt, the salt is per user. If the user is using the same password between the two, that's not a weakness of bcrypt for the more secure site. In fact, even if you use bcrypt and 15 peppers on the more secure site, if the password is the same, the password is the same.
Re:Peppering (Password Storage)
by willcipriano on Thursday July 02, 2020 @07:17AM
(#23711993)
Fair enough, but adding a database column and generating a random string on registration is perhaps a 10 minute job and provides all those benefits plus many more.
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.
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.
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).
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:
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?
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.
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.
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.