Background
As far as I know, the recommended/approved method for storing password verifiers is to store:

$verifier = $salt + hash( $salt + $password )

Where:

hash() is a cryptographic hashing algorithm

$salt is a random, evenly distributed, high entropy value

$password is the password entered by the user

Some people advice to add a secret key into the mix (sometimes called pepper). Where the pepper is a secret, high entropy, system-specific constant.

The rationale seems to be that even if the attacker gets hold of the password verifiers, there is a good chance he or she does not know the pepper value. So mounting a successful attack becomes harder.

So, my question is:
Does adding a pepper value in addition to a salt when hashing passwords increase the overall security?

Or is the perceived increased security based on false assumptions?

Quick Update
I know the purpose of the $salt (I wrote quite a long answer on StackOverflow about it) the additional $pepper key is not improving upon what the salt does.
The question is, does the $pepper add any security other than what the salt does?

If I understand correctly, and this was discussed in the q I linked to, a pepper can "...help mitigate certain compromise scenarios (eg, database backups going missing) by having a piece of the puzzle stored elsewhere" (@RoryMccune). Also can differentiate your hashes from someone elses - IF a big enough rainbow table is ever constructed...
– AviD♦Apr 22 '11 at 14:15

3

This question asks specifically about pepper. For the broader picture, see a more complete discussion elsewhere on IT Security StackExchange of the more general topic of password hashing
– nealmcbMay 14 '11 at 19:23

2

@commonSenseCode, yes, plenty of arguments can be brought against using a pepper. But having a lot of arguments does not automatically make a better point. In the case of the pepper, it is by now quite widely accepted as a method that increases security, when used in the right context. Since 2017, even NIST started to recommend it.
– JaccoMay 22 '18 at 7:01

7 Answers
7

As a typical example, let's say you're building a web application. It consists of webapp code (running in some webapp framework, ASP.NET MVC, Pyramid on Python, doesn't matter) and a SQL Database for storage. The webapp and SQL DB run on different physical servers.

The most common attack against the database is a successful SQL Injection Attack. This kind of attack does not necessarily gain access to your webapp code, because the webapp runs on a different server & user-ID.

You need to store passwords securely in the database, and come up with something on the form of:

$hashed_password = hash( $salt . $password )

where $salt is stored in plaintext in the database, together with the $hashed_password representation and randomly chosen for each new or changed password.

The question is then, given that it is almost zero effort to add a constant value to the application code, and that the application code will typically not be compromised during an SQL Injection Attack, is the following then substantially better than the above?

$hashed_password = hash( $pepper . $salt . $password )

where $salt is stored in plaintext in the database, and $pepper is a constant stored in plaintext in the application code (or configuration if the code is used on multiple servers or the source is public).

Adding this $pepper is easy -- you're just creating a constant in your code, entering a large cryptographically secure random value (for example 32byte from /dev/urandom hex or base64 encoded) into it, and using that constant in the password hashing function. If you have existing users you need a migration strategy, for example rehash the password on the next login and store a version number of the password hashing strategy alongside the hash.

Answer:

Using the $pepperdoes add to the strength of the password hash if compromise of the database does not imply compromise of the application. Without knowledge of the pepper the passwords remain completely secure. Because of the password specific salt you even can't find out if two passwords in the database are the same or not.

The reason is that hash($pepper . $salt . $password) effectively build a pseudo random function with $pepper as key and $salt.$password as input (for sane hash candidates like PBKDF2 with SHA*, bcrypt or scrypt). Two of the guarantees of a pseudo random function are that you cannot deduce the input from the output under a secret key and neither the output from the input without the knowledge of the key. This sounds a lot like the one-way property of hash functions, but the difference lies in the fact that with low entropy values like passwords you can effectively enumerate all possible values and compute the images under the public hash function and thus find the value whose image matches the pre-image. With a pseudo random function you cannot do so without the key (i.e. without the pepper) as you can't even compute the image of a single value without the key.

The important role of the $salt in this setting comes into play if you have access to the database over a prolonged time and you can still normally work with the application from the outside. Without the $salt you could set the password of an account you control to a known value $passwordKnown and compare the hash to the password of an unknown password $passwordSecret. As hash($pepper . $passwordKnown)==hash($pepper . $passwordSecret) if and only if $passwordKnown==$passwordSecret you can compare an unknown password against any chosen value (as a technicality I assume collision resistance of the hash function). But with the salt you get hash($pepper . $salt1 . $passwordKnown)==hash($pepper . $salt2 . $passwordSecret) if and only if $salt1 . $passwordKnown == $salt2 . $passwordSecret and as $salt1 and $salt2 were randomly chosen for $passwordKnown and respectively $passwordSecret the salts will never be the same (assuming large enough random values like 256bit) and you can thus no longer compare password against each other.

Hmm, the edit removed the much of the uncertainty present in the answer as written by @Jesper Mortensen. Maybe the edit should be rolled back and moved to a separate answer?
– JaccoApr 23 '11 at 22:08

10

The gist of the argument here is pretty good and I tend to agree. However don't put a constant in the code if you can avoid it, make it a configuration variable of some kind (obv. not stored in the DB, but not in the code either).
– frankodwyerApr 24 '11 at 9:30

@Jacco: I agree that the tone of this answer has changed with the edits done. But then, looking at who made the edits, I'm now more comfortable that this answer is correct (within its scope), so I'm happy to leave it as it is now. :-)
– Jesper MortensenApr 24 '11 at 11:27

15

I agree with the statement: that it is almost zero effort to add a constant value. It really is, so albeit not as critical as salting a password, I see no reason to exclude peppering in some form to provide an application code level protection.
– foochowFeb 11 '14 at 18:20

3

@frankodwyer, You could even have both. Constant in the code on the webserver plus another constant in a "config" entity on another server.
– PacerierDec 2 '14 at 7:40

(Note: using a salt is only half of the job; you also need to make the hash function slow -- so that attacking a single low-entropy password is still difficult. Slowness is usually achieved through multiple iterations, or hashing the concatenation of 10000 copies of the salt and password.)

What your "pepper" does is that it transforms the hash into a MAC. Making a good, secure MAC out of a hash function is not easy, so you'd better use HMAC instead of a homemade construct (the theoretical way of putting it is that a collision-resistant hash function is not necessarily indistinguishable from a random oracle).

With a MAC, you may gain some security in the following sense: possibly, database read access by the attacker could cease to be a real problem. The MAC key (the "pepper") may concentrate the confidentiality need. However, this relies on the MAC being also a one-way function, which is a property which you will get from many MAC constructions (including HMAC) but which is not really guaranteed cryptographically speaking (there are subtleties).

The "pepper" implies that you have a key to manage, including secure storage in a way which resists to reboots. A key is small and fits in RAM, but, due to the storage requirements, it is unclear whether it actually improves security. An attacker who can read the whole database usually can also read the whole harddisk, including any "protected" file. The key small size may allow for some advanced setups, e.g. the key being stored on smartcards which are used at boot time but not left connected afterwards. To sum up, whether peppering is worth the effort thoroughly depends on the context -- on a general basis, I would recommend against it, in order to avoid the added complexity.

@Jacco: if the hash function is any good, then no, you do not weaken it in any way by adding a known prefix or suffix. However, being a good hash function is not fully equivalent to being a good MAC (the difference is subtle but real), hence the need to follow well-studied constructions such as HMAC. As for a good book, you can try the Handbook of Applied Cryptography ( cacr.math.uwaterloo.ca/hac )(not the same book than "Applied Cryptography" by Schneier).
– Thomas PorninApr 22 '11 at 20:54

5

@Jesper: we are talking of the same thing, but with distinct viewpoints. The "pepper" increases security only insofar as it is not known by the attacker -- whether this is true or not is not easy to guess, especially in your setup in which the webapp and the database are on distinct machines. The pepper will not decrease security by itself, except that it increases installation complexity (a value which must not be known by the attacker is a key, and key management is known to be non-immediate). Also, with a secret pepper, security increase depends on whether the construct is a good MAC.
– Thomas PorninApr 23 '11 at 14:09

44

-1 for the comment "An attacker who can read the whole database usually can also read the whole harddisk". Clearly you are not a penetration tester, don't make such assumptions.
– rookJul 19 '12 at 9:23

@rook, it's better to point out the flaw in the answer than to attack the writer's credentials, as pointing out the flaw increases the information with which other users can judge the answer. I will do that here. It is not true that database credentials are usually equal to whole OS access. DB servers are frequently separated from the application running them, which connects over the network. Or flaws in the DB software might lead to full access to the DB, even if the DB user cannot break out of its sandbox.
– madumlaoMar 19 '16 at 10:17

When does a pepper help?

As the others already pointed out, adding a pepper is only an advantage, as long as the attacker has access to the hash-values in the database, but has no control over the server, and therefore does not know the pepper. This is typical for SQL-injection, probably one of the more often used attacks, because it is so easy to do.

What does a pepper improve?

$hashValue = bcrypt('12345', $cost, $salt);

This password you can get easily with a dictionary attack, even if you correctly used a slow key-derivation function. Put the most used passwords into a dictionary and brute force with this weak passwords. It's very likely that we find the password in (too) many cases.

$hashValue = bcrypt('12345anm8e3M-83*2cQ1mlZaU', $cost, $salt);

With the pepper, the weak password grows in length, it contains now special characters, and more important, you will find it in no dictionary. So, as long as the pepper stays secret, it does prevent dictionary attacks, in this case it can protect weak passwords.

Edit:

There is a better way to add a server side key, than using it as a pepper. With a pepper an attacker must gain additional privileges on the server to get the key. The same advantage we get by calculating the hash first, and afterwards encrypting the hash with the server side key (two way encryption). This gives us the option to exchange the key whenever this is necessary.

@Jacco - Just added an example of using a hmac.
– martinstoeckliNov 2 '12 at 21:42

I'm not sure in what language your example is, but it would be more clear if it where language agnostic
– JaccoNov 5 '12 at 7:23

@Jacco - It is PHP, but the function bcrypt is fictitious, because PHP itself does not provide such a function yet (before 5.5), the function hash_hmac exists though. I tried to edit the answer, so all confusing parts are removed, what do you think i could improve more?
– martinstoeckliNov 5 '12 at 8:00

The first eight characters of the user’s password are used
as a key for the DES; then the algorithm is used to encrypt a constant. Although this constant is zero at the
moment, it is easily accessible and can be made installation-dependent.

... A keyed hash function (e.g., HMAC [FIPS198-1]), with the key stored separately from the hashed authenticators (e.g., in a hardware security module) SHOULD be used to further resist dictionary attacks against the stored hashed authenticators.

Wow, I've been advocating for pepper for years but people rarely implement it for no apparent reason. I didn't know NIST now also recommends it! Thanks for this answer, this should be higher up.
– LucOct 11 '18 at 8:43

I am about to break into a website X using an SQL injection to retrieve a list of users, with their password hashes and salt. Suppose website X is also using a global pepper.

All I would have to do would be to register a user at website X with a user name and password known to me prior to the SQL injection. I would then know, for a particular record in the database, the password hash, the plain text password, the salt (stored as plain text) and it would be computationally trivial for me to crack the global pepper on the basis of this one record.

So really, a pepper would be a way of slowing an attacker down for a trivial amount of overhead time. They wouldn't have to brute force the password + salt + pepper, as intended, only the pepper.

The above is a form of chosen plaintext attack. As long as attackers know the algorithm (hash()), the output ($hashed_password), and all but one of the inputs ("constants" $salt & $password and "variable" $pepper), they can "solve for x" like a linear algebra equation (h=s+p+x == h-s-p=x), but by brute force of course. Making the pepper longer than 56 bytes (448 bits), bcrypt's limit, increases the time cost and is as good as bcrypt but still may not be as good as scrypt. So, as long as the pepper is sufficiently long, it is an improvement.

What if the pepper is a 128 bit random value? Is it still trivial to crack it on the basis of salt and plaintext password value? The reason why salt and pepper values are helpful is because you can easily make it long enough (128 bit) and you don't have to remember it that is why cracking it through brute force is never trivial.
– void_inAug 31 '13 at 10:25

1

If the pepper is a 128 bit random value then it certainly won't still be trivial to crack. However you have to remember the following: - you are adding this value to the overhead of the hashing function on every single validation - the pepper is global and only needs to be brute force cracked once, not once for every record - you are much better off increasing the length of the salt than the pepper
– Alex RMar 10 '15 at 22:09

5

The cost of any even partially appropriate hash function like bcrypt, script, or even multiple iterations of some sha, does not depend on the length of the key, so this is wrong. If the hash function is chosen correctly, and not just using a single iteration of sha256, the pepper merely needs to boost the initial password to 50 bits of entropy to make it bullet proof for decades if not centuries. Of course, like @void_in said, we boost it to 128 bit or 256 bit, just because we can.
– PeterDec 10 '15 at 20:00

Not very familiar with how a server would be able to hide a global pepper constant but my take is that sooner or later a hacker that has penetrated the server will figure out how to capture the pepper value.
To make a pepper value totally secure would require special hardware. One way to do this would be to use a FPGA board installed in the server. The FPGA would contain the code used to perform the hash including the pepper value and all hash calculations occur inside the FPGA. With the FPGA, the programming can be a one way function. The pepper can be programmed in but there is no instruction that can be sent to read it back out. The pepper would be stored on a piece of paper locked in a safe.
If the pepper is 128+ bits generated randomly, there would be no practical way of determining it.
Not sure how practical this would be as it would increase server hardware cost.