I've read quite a bit of the StackExchange and HackerNews debates on the user of "peppers" in password hash security. There are a number of different implementations of the idea of a pepper, ranging from an additional hardcoded salt in the code hash(password, pepper, salt), to encrypting each password hash separately, in which case the secret key is the pepper.

In the case of one of the middle ground approaches, a shared and secret pepper is included via hash(hmac(password, pepper),salt). This is necessary primarily because of many hashing algorithm's reliance on the Merkle–Damgård construction which is vulnerable to length extension attacks.

However, Argon2 is not based on a Merkle–Damgård construction and therefore this attack would not apply. In using Argon2, can the naive approach of argon2(password, pepper, salt) be used?

Additionally, the Argon2 specification Introduction seems to indicate that using HMACs, peppers, or secret keys at all is unnecessary. Is Argon2ID with strong memory, threads, and time costs enough on its own?

  • Read the length extension attack. It is not a collision attack.
    – kelalaka
    Commented Mar 26, 2019 at 20:47
  • @kelalaka So HMAC is overkill? Just hash(password + salt + pepper) should be enough?
    – ManRow
    Commented Apr 7, 2020 at 14:46

2 Answers 2


Argon2 actually allows a pepper in the algorithm itself, called the secret. This would be the ideal way to use a pepper, unfortunately most language bindings I've looked at don't expose this parameter (of the bindings listed on GitHub only 2 of them specifically mention supporting keyed hashing).

If the secret parameter can't be used, argon2(hmac(pepper, password)) is a reasonable alternative. argon2 can be applied outside hmac for simplicity, as the way it's generally used it'll give you an output that includes an encoding of the salt and options used. Using hmac outside argon2 should be comparable for security, but then you'd need to keep track of the Argon2 parameters yourself.

As for the spec seeming to indicate that a pepper isn't necessary, I assume you're referring to this:

A trivial solution for password hashing is a keyed hash function such as HMAC. If the protocol designer prefers hashing without secret keys to avoid all the problems with key generation, storage, and update, then he has few alternatives: the generic mode PBKDF2, the Blowfish-based bcrypt, and scrypt.

The way I read it, what that's saying is that a keyed hash is actually the best way to hash passwords, so long as you protect the key really well (such as using an HSM). The problem is, protecting the key is difficult (HSMs cost money, supporting a pepper with an HSM takes development time), so slow hashes like scrypt, bcrypt, and Argon2 are used instead.

What it's not saying is that using a keyed hash in addition to Argon2 is a bad idea. Even if you can't protect the key with an HSM, it can still be worthwhile if it's better protected than the password hashes. The worst passwords will always be cracked no matter how high you set the cost (and your users' patience gives you a low upper limit); a pepper can prevent that.


Edit: Strike this entire comment. My approach isn't wrong, per se, but argon2 already offers a secret parameter. Just use this.

The simplest approach to this—and the one on the strongest cryptographic footing—is (simplistically) HMAC(key, argon2(password)). This also still allows the expensive argon2 calculation to be performed client-side for server-relief situations.

  • Is the assumption here that there will be one key or many?
    – Prime
    Commented Mar 26, 2019 at 21:14
  • 1
    And shouldn't I put the HMAC "inside" of the Argon hash instead of vice versa?
    – Prime
    Commented Mar 26, 2019 at 21:20
  • 1
    Re: inside or outside, inside prevents server relief, since argon2(HMAC(key, password)) requires the password to be HMAC'd server-side first. With HMAC(key, argon2(password)), a client can compute argon2(password) first before sending to the server. This approach also has the secondary advantage of ensuring that the server never actually sees the user's raw password, so there's no chance of it hitting a log file. Commented Mar 26, 2019 at 21:38
  • 1
    But lastly, argon2 already provides a secret parameter. Really, really, really just use this. Commented Mar 26, 2019 at 21:38
  • 1
    Unfortunately, it's not supported natively in my language. Adjacently, does Argon explain how they use the optional secret in computing the hash? I couldn't find that anywhere.
    – Prime
    Commented Mar 26, 2019 at 21:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .