
Peppering (Password Storage) - jdnier
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#peppering
======
stouset
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.

~~~
linsomniac
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.

~~~
stouset
That’s fair. You can always HMAC a second time to rotate the pepper, however.

------
Waterluvian
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.

------
lawnchair_larry
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.

~~~
Jwarder
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.

------
rlpb
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.

~~~
stouset
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.

------
noodlesUK
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.

~~~
sroussey
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.

~~~
GoblinSlayer
The salt is public by design, it's not intended to be secret, only unique. For
pepper secrecy is essential.

~~~
ballenf
It should have been called "spice" or "herb". Instead of the name of a single,
known raw ingredient.

------
tzs
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?

~~~
Tostino
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.

------
Puts
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.

~~~
GoblinSlayer
You can apply several peppers, they are composable, use all you have.

------
jwlake
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.

~~~
motohagiography
Was going to say this was the "diversification component," in KDFs. Is someone
taking credit for coining pepper, or is it different?

------
gruez
It's puzzling why a security resource would use vague terms like "x characters
long" to characterize entropy.

>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?

------
willcipriano
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.

~~~
georgyo
Your pepper is always static, and your salt becomes static. Wouldn't that
essentially be peppered even without the pepper?

I don't see any value of a pepper. It doesn't actually add any protection.

~~~
willcipriano
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.

~~~
Sherl
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.

------
pontifier
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.

------
simonebrunozzi
> One solution to this is to store the ID of the pepper in the database
> alongside the associated password hashes

Wouldn't this expose the pepper to Database attacks (SQL injections, etc), and
invalidate the reason for using it in the first place?

~~~
yencabulator
ID of the pepper, not the pepper itself.

------
Ayesh
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.

~~~
twhitmore
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.

~~~
theginger
> I prefer the approach where they use the pepper to symmetrically encrypt &
> decrypt the hash, which potentially allows the pepper to be rotated.

Isn't that field level encryption rather than pepper?

~~~
dylz
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

~~~
Tostino
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.

------
kissgyorgy
1Password does this. They generate a very long secret key beside your
password.

------
GoblinSlayer
To prevent hash reuse you can hash the password with a constant per site salt,
e.g. bcrypt(sha256("news.ycombinator.com",password))

~~~
stouset
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).

~~~
Sherl
Yes some implementation consider atomic time apart from thermal noises when
generating them.

