Using peppers with Crypt::Passphrase


Crypt::Passphrase is a module for managing passwords. It allows you to separate policy and mechanism, meaning that the code that polices authorization doesn’t have to know anything about what algorithms are used behind the screen, and vice-versa; thus making for a cryptographically agile system.

It’s not only handling the technical details of password hashes for you but also it deals with a variety of schemes. It’s especially useful for transitioning between them.

A configuration might look like this (Koha):

my $auth = Crypt::Passphrase->new(
    encoder => {
        module => 'BCrypt',
        cost   => 8,
    validators => [ 'MD5::Base64' ],

Using it might look like this:

if (!$auth->verify_password($password, $hash)) {
    die "Invalid password";
elsif ($auth->needs_rehash($hash)) {
    my $new_hash = $auth->hash_password($password);

It supports a variety of algorithms, but argon2 and bcrypt are by far the most popular ones. That said, it can do much more that that: it can do peppers for you.

Why Peppers

The function of peppers is to protect leaked passwords, especially the bad ones. Password hashes try making brute-force attacks so expensive that attackers won’t even bother, but in the end they can’t really protect passwords from a dictionary attack. The key-space of bad passwords is so small that you can’t really prevent that.

When you add a pepper, that means an attacker needs to brute-force both password and pepper, but because the pepper doesn’t need to be memorized by a human it can actually be a piece of high-entropy (e.g. a 16 or 32 byte chunk of good randomness). That would make it well outside of reach of any brute force attack for sheer physical reasons.

The most important thing you must understand about peppers is that like passwords the security they provide hinges entirely on their secrecy. If that secrecy is compromised they don’t do anything for security. If you remember nothing else of this blog post, please remember that.

Mapping Peppers

The first thing you’d probably notice about my modules is that you don’t pass it a pepper, but a map of peppers. This is an essential quality of the system that a lot of naive pepper implementations are lacking. Peppers are keys, and all keys must be rotatable. Like passwords, you need to be able to change them if they may have been compromised. By using a map and adding the identifier in the metadata section of the hash, you can rotate in a new key while still able to check old ones. This gives the system the agility it needs to …

Style of peppers

The second thing you might notice is that I provide two very different styles of peppering; using some sort of MAC before the password hash, and using symmetric encryption after the password hash (e.g. Crypt::Passphrase::Argon2::AES and Crypt::Passphrase::Bcrypt::AES). The former approach appears to be more common out in the wild, but that latter is by far the better one. Firstly because its security is easily provable (it hinges only on symmetric encryption, not on an unusual combination of constructs), but secondly because it allows for easy re-peppering without needing the user’s password to recompute the password hash inside of it (essentially just decrypting with the old key and encrypting with the new one). For that reason I would strongly recommend the latter approach.

Using peppers

It can be as simple as this:

my $auth = Crypt::Passphrase->new(
    encoder => {
        module => 'Argon2::AES',
        peppers => \%peppers,

All you really need to do is change the module name and pass in the peppers. The hardest part of it is probably securely storing the peppers. There are many tools to help you with this (e.g. vault, sealed secrets, and/or my own Mojolicious::Plugin::Credentials). How to best do this really depends on your setup.

Arguably the best option is using a hardware security module (e.g. CP::Argon2::HSM), but few people has a hardware security module laying around (good ones are rather expensive, though you might convince your TPM2 to function as one).


Using peppers doesn’t have to be that hard. If you have an appropriate credentials store, you can easily add it to your application and enhance the security of your passwords. Maybe you too should give Crypt::Passphrase::Argon2::AES a try.

1 Comment

Friendly suggestion, the term "recalling" makes it sound like the article is about to tell us to not use the module (as in to "recall" a product). Maybe just call it, "Peppers in Crypt::Passphrase". Idk, but that's what the title suggested to me.

Leave a comment

About Leon Timmermans