Password Hashing: add salt + pepper or is salt enough?
In some circumstances, peppers can be helpful.
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 most important aspect of every password hashing scheme is that hash
is a slow cryptographically secure hash function, see https://security.stackexchange.com/a/31846/10727 for more background knowledge.
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 $pepper
does 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.
(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.
I would like to point out what a pepper really can do.
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.
$hash = bcrypt($passwort, $salt);
$encryptedHash = encrypt($hash, $serverSideKey);