Want to learn object-oriented programmming? Get my free course

Using the chain-of-responsibility pattern to hash WordPress passwords

Passwords Evolved” is an open source WordPress plugin that I’ve built and maintained over the years. It’s a security plugin that prevents someone from using a password that has appeared in a data breach. To do that, it leverages the “Have I Been Pwned?” API to check if the password you’re using was found in a breach.

The plugin also changes the hashing function used to hash WordPress passwords. Initially, the plugin only supported the bcrypt hashing function. This hashing function has been standard since PHP 5.5.

Since PHP 7.0, it’s been possible to hash passwords using a newer function called Argon2. Argon2 is available natively since PHP 7.2. Before that, you had to use the libsodium library to use Argon2 with PHP. There’s also a libsodium compatibility layer starting in WordPress 5.2.

Checking for and using all these different hashing functions sounds like a headache waiting to happen. But, in fact, it’s not that hard to design a set of classes to handle all these different cases. The “Chain-of-Responsibility” pattern was created for situations such as this one.

What is the “Chain-of-Responsibility” pattern?

So “Chain-of-Responsibility” sounds like a mouthful, but it’s not too complicated in practice. A good analogy for it is the if ... elseif ... elseif conditionals chain. The “Chain-of-Responsibility” pattern is basically the object-oriented version of that chain. (Thus the name!)

So that’s the idea with the “Chain-of-Responsibility” pattern. You have a chain of objects. And whenever you want to perform an operation on some input, you pass it to that chain. The chain will pass it from one object to the next until one of them can perform an operation on it. Just like you would with that if ... elseif ... elseif chain.

The diagram above shows the chain in action. The chain has three objects, and the input passes from object to object. But since the second object decides to perform an action on the input, the third object never gets to interact with it.

This highlights the big difference between the “Chain-of-Responsibility” pattern and the better-known decorator pattern. With the decorator pattern, every object gets to interact and “decorate” the input. It’s not just one object that gets to interact with it.

Besides this, both patterns work nearly identically. So, if you’re already familiar with the decorator pattern, the inner workings of the “Chain-of-Responsibility” pattern won’t surprise you. Both patterns have a similar implementation.

Starting with an interface

The core element of this implementation of the “Chain-of-Responsibility” pattern is going to be an interface. For the “Passwords Evolved” plugin, that interface was the PasswordHasherInterface interface. Here’s a look at it:

/**
 * A password hasher hashes a password using a hashing algorithm.
 */
interface PasswordHasherInterface
{
    /**
     * Hashes the given password. Returns null if unable to hash password.
     *
     * @param string $password
     *
     * @return string|null
     */
    public function hash_password($password);

    /**
     * Checks if the password hasher supports the given hash for verification.
     *
     * @param string $hash
     *
     * @return bool
     */
    public function is_hash_supported($hash);

    /**
     * Checks if the given hash is valid. If a hash is invalid, we need to rehash it.
     *
     * @param string $hash
     *
     * @return bool
     */
    public function is_hash_valid($hash);

    /**
     * Validates that the given password matches the given hash.
     *
     * @param string $password
     * @param string $hash
     *
     * @return bool
     */
    public function is_password_valid($password, $hash);
}

The PasswordHasherInterface (shown above) has four methods. Three of them perform actions on a hash value or password. These are the inputs that we described earlier.

Those three action-performing methods are the hash_password, is_hash_valid and is_password_valid methods. The hash_password method takes a password turns it into a hash value. The is_hash_valid method checks if the given hash value is a valid hash value or not. The is_password_valid method checks if the given password is valid for the given hash value.

The fourth method in the PasswordHasherInterface interface is the is_hash_supported method. This method is central to this implementation of the “Chain-of-Responsibility” pattern. It’s the method used to determine if our object in the chain can perform actions on the given hash.

Whenever you use any of the three action-performing methods, the chain will start calling the is_hash_supported method on all the objects in the chain. The first object in the chain that returns true will be the one selected to perform the requested action. The value returned by the action is the one returned by the chain.

Creating some password hashers

Now that we have the PasswordHasherInterface interface, we can move on to designing our classes. Before we create our chain, we’ll create some password hashers that we can use with it. For this article, we’ll create two.

WordPress password hasher

The first password hasher that we’ll create will hash passwords using WordPress’s built-in PasswordHash class. We’re going to call it WordPressPasswordHasher. You can see the code for it below.

/**
 * Password hasher that uses the builtin WordPress password hasher.
 */
class WordPressPasswordHasher implements PasswordHasherInterface
{
    /**
     * WordPress password hasher.
     *
     * @var \PasswordHash
     */
    private $wordpress_hasher;

    /**
     * Constructor.
     *
     * @param \PasswordHash $wordpress_hasher
     */
    public function __construct(\PasswordHash $wordpress_hasher)
    {
        $this->wordpress_hasher = $wordpress_hasher;
    }

    /**
     * {@inheritdoc}
     */
    public function hash_password($password)
    {
        return $this->wordpress_hasher->HashPassword($password);
    }

    /**
     * {@inheritdoc}
     */
    public function is_hash_supported($hash)
    {
        return 0 === strpos($hash, '$P$');
    }

    /**
     * {@inheritdoc}
     */
    public function is_hash_valid($hash)
    {
        return false;
    }

    /**
     * {@inheritdoc}
     */
    public function is_password_valid($password, $hash)
    {
        return $this->wordpress_hasher->CheckPassword($password, $hash);
    }
}

Wrapper methods

The WordPressPasswordHasher class is just a wrapper around the PasswordHash class. This means that, for a lot of PasswordHasherInterface methods, we’re just going to call a PasswordHash method. (This is why it’s a good example of the adapter pattern!) Because of that, the WordPressPasswordHasher constructor will just take a PasswordHash object as an argument. We then assign it to the wordpress_hasher class property.

Next, let’s look at the implementations of the PasswordHasherInterface methods. Two of them call a method on the PasswordHash object stored inside our WordPressPasswordHasher object. There’s hash_password which calls the HashPassword method of the PasswordHash object. And then there’s is_password_valid which calls the CheckPassword method.

The two more interesting methods to discuss are is_hash_supported and is_hash_valid. The is_hash_supported method uses the strpos function to check if a hash starts with $P$. But what does $P$ mean and why are we looking for it?

The reason why we look for $P$ is because of how hash functions work. Each function will prefix a hash value that it generates. That way, you can tell it created the hash value. For the PasswordHash class, that prefix is $P$ and that’s why we’re looking for it.

The last method is is_hash_valid. You might be wondering why it returns false without even looking at the hash value. This is intended because of what the plugin wants to do. It wants WordPress passwords to use a stronger hashing function.

So by always returning false, we’re forcing the plugin to discard any hash value created by the PasswordHash class. Instead, the plugin will have to rehash the password using a stronger hashing function. This might seem weird but it’ll make more sense once we go over how the “chain” works later in the article.

Native password hasher

For the second password hasher, we’re going to use PHP’s native password hashing functions. These functions can use different hashing algorithms such as Argon2. But by default, they use bcrypt.

As for the class, we’ll name it NativePasswordHasher. It’ll also implement the PasswordHasherInterface interface. (All classes in the chain should.) Here’s what the code for the class.

/**
 * Password hasher that uses the native PHP password hashing functions.
 */
class NativePasswordHasher implements PasswordHasherInterface
{
    /**
     * Algorithm used when hashing a password.
     *
     * @var int
     */
    private $algorithm;

    /**
     * Constructor.
     */
    public function __construct()
    {
        $this->algorithm = PASSWORD_DEFAULT;

        if (defined('PASSWORD_ARGON2ID')) {
            $this->algorithm = PASSWORD_ARGON2ID;
        } elseif (defined('PASSWORD_ARGON2I')) {
            $this->algorithm = PASSWORD_ARGON2I;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function hash_password($password)
    {
        return password_hash($password, $this->algorithm);
    }

    /**
     * {@inheritdoc}
     */
    public function is_hash_supported($hash)
    {
        if (0 === strpos($hash, '$argon2id$')) {
            return defined('PASSWORD_ARGON2ID');
        } elseif (0 === strpos($hash, '$argon2i$')) {
            return defined('PASSWORD_ARGON2I');
        }

        return 0 === strpos($hash, '$2');
    }

    /**
     * {@inheritdoc}
     */
    public function is_hash_valid($hash)
    {
        return !password_needs_rehash($hash, $this->algorithm);
    }

    /**
     * {@inheritdoc}
     */
    public function is_password_valid($password, $hash)
    {
        return password_verify($password, $hash);
    }
}

Choosing an algorithm

The interesting part of the NativePasswordHasher class is the constructor. This is where we’re going to choose the hashing algorithm used by the class. We’ll store that hashing algorithm value in the algorithm class property.

Choosing the algorithm is simply a question of detecting PHP password constants using the defined function. By default, we’re going to assign the PASSWORD_DEFAULT constant value to the algorithm property. This constant stores the default hashing algorithm used by PHP.

At this time, this default algorithm is bcrypt. We could have also just used its constant called PASSWORD_BCRYPT instead. But, either way, both PASSWORD_DEFAULT and PASSWORD_BCRYPT are identical.

The rest of the constructor is just two guard clauses. They check if PHP supports one of the two possible Argon2 hashing algorithms. If PHP does, we change the value of the algorithm property.

The first constant that we look for is PASSWORD_ARGON2ID. This is the preferred Argon2 hashing algorithm. That said, it’s only available starting with PHP 7.3. So, to support PHP 7.2, we check for the PASSWORD_ARGON2I constant as well.

Using the algorithm

Now that we set the algorithm class property, we can look at how we use it in the NativePasswordHasher class. The two methods that make use of it are the hash_password and is_hash_valid methods. Both use the algorithm class property as the second argument of a PHP password function.

These two functions are password_hash and password_needs_rehash. The hash_password method uses the password_hash function to create a hash value using our chosen algorithm. Meanwhile, the is_hash_valid uses the password_needs_rehash function to check if a hash value is valid for our chosen algorithm.

Checking the hash

The last two methods don’t use the algorithm. The is_hash_supported method is very similar to the one in the WordPressPasswordHasher class. We use it to check if the NativePasswordHasher supports the given hash value.

The is_hash_supported method is a bit more complex than the one in the WordPressPasswordHasher class. That’s because we have to check for three hash prefixes. Two prefixes are for the two Argon2 algorithms and the third one is for bcrypt.

Checking if we support Argon2 algorithms is done in two steps. First, we check if the hash value contains the algorithm prefix. If it does, we check if the password constant is defined using the defined function. If we get to the end of the method, we check for the bcrypt prefix only since it’s the default algorithm.

Verifying the password also doesn’t require the algorithm class property. All that you have to do is pass the password and hash value to the password_verify password function. password_verify will figure out which algorithm to use by checking the hash value prefix like we’ve been doing.

Designing the chain

Now that we have some password hashers, we can move on to designing our chain. We’re going to call it PasswordHasherChain. Unlike our password hashers, we’re going to take a bit more time explaining the design of the class.

/**
 * Manages a chain of password hashers.
 */
class PasswordHasherChain implements PasswordHasherInterface
{
}

But first, let’s start by creating the empty PasswordHasherChain class. It implements the PasswordHasherInterface interface that our password hashers also implement. The fact that all these classes share the same interface is one of the most crucial aspects of the design.

Constructor

To begin, we’re going to add the constructor to our PasswordHasherChain.

/**
 * Manages a chain of password hashers.
 */
class PasswordHasherChain implements PasswordHasherInterface
{
    /**
     * The password hashers that the chain handles.
     *
     * @var PasswordHasherInterface[]
     */
    private $password_hashers;

    /**
     * Constructor.
     *
     * @param PasswordHasherInterface[] $password_hashers
     */
    public function __construct(array $password_hashers = [])
    {
        $this->password_hashers = $password_hashers;
    }

    // ...
}

You can see above that the constructor has a single parameter called password_hashers. We assign it to a class property also called password_hashers. We type hinted the password_hashers constructor parameter as an array with a default of an empty array.

More type safety

The problem with an array type hint is that it doesn’t give us a lot of type safety. The password_hashers array can contain any type of value. But we want it to contain only PasswordHasherInterface objects.

So we’re going to add some extra type safety to our constructor. We’re going to do that by running the password_hashers array through an array function. For what we’re trying to do, that array function will be the array_filter function.

/**
 * Manages a chain of password hashers.
 */
class PasswordHasherChain implements PasswordHasherInterface
{
    /**
     * The password hashers that the chain handles.
     *
     * @var PasswordHasherInterface[]
     */
    private $password_hashers;

    /**
     * Constructor.
     *
     * @param PasswordHasherInterface[] $password_hashers
     */
    public function __construct(array $password_hashers = [])
    {
        $this->password_hashers = array_filter($password_hashers, function ($password_hasher) {
            return $password_hasher instanceof PasswordHasherInterface;
        });
    }

    // ...
}

Above is the updated PasswordHasherChain constructor using the array_filter function. The array_filter function will remove all array elements that our callback returns false for. Here, that callback is an anonymous function.

This anonymous function receives a password_hasher as an argument. It’ll then determine whether password_hasher is an instance of PasswordHasherInterface or not. It does this by using the instanceof operator. The operator will return true if password_hasher is an instance of PasswordHasherInterface. Otherwise, it’ll return false.

We assign the array returned by the array_filter function to password_hashers array class property like before. But now, we’re sure that the array only contains PasswordHasherInterface objects. This will allow us to use it without worrying that there might be invalid data in it.

Selecting a password hasher

The use of the array_filter function in the PasswordHasherChain class constructor was really just the beginning. Now that we have a clean password_hashers array class property, we’re going to use array functions with it as well! To begin, let’s look at how we can select a password_hasher from the password_hashers array.

/**
 * Manages a chain of password hashers.
 */
class PasswordHasherChain implements PasswordHasherInterface
{
    // ...

    /**
     * Get the password hasher that supports the given hash.
     *
     * @param string $hash
     *
     * @return PasswordHasherInterface|null
     */
    private function get_password_hasher($hash)
    {
        return array_reduce($this->password_hashers, function ($found, PasswordHasherInterface $password_hasher) use ($hash) {
            if (!$found instanceof PasswordHasherInterface && $password_hasher->is_hash_supported($hash)) {
                $found = $password_hasher;
            }
            return $found;
        });
    }
}

The method that we’re creating to do that is called get_password_hasher. The method that you can see above has a single parameter: hash. This is the hash value that we want the returned password hasher to support.

Using array_reduce to find a password hash

To find that password hasher, we’re going to use the array_reduce function. This array function reduces an array to a single value using a callback. The callback will again be an anonymous function.

This anonymous function has two parameters: found and password_hasher. found is the current value that array_reduce would return if this was the last array item. password_hasher is the current password hasher from the password_hashers array that we’re evaluating.

Now, let’s look at the code inside the anonymous function. It only contains a single conditional that we use to assign the password_hasher to the found variable. This conditional checks for two things.

First, it checks if we found a password hasher already. We do that by checking if found is an object implementing the PasswordHasherInterface. The reason for this check is that we don’t want to overwrite the found value if we already found a password harsher.

If we still haven’t found a password hasher, we move on to the second check. That’s calling the is_hash_supported method of the given password_hasher. If the method returns true, we assign the password_hasher to the found variable and return it.

Once array_reduce has finished going through the array of password hashers, it’ll return one of possible two results. Either we found a password hasher, so the result is a class implementing the PasswordHasherInterface interface. Or it’s null because we didn’t find one.

Using the get_password_hasher method

The get_password_hasher method is central to the PasswordHasherChain class. Two of the PasswordHasherInterface interface methods need us to find the correct password hasher for the given hash value. Those two methods are is_hash_supported and is_hash_valid.

/**
 * Manages a chain of password hashers.
 */
class PasswordHasherChain implements PasswordHasherInterface
{
    // ...

    /**
     * {@inheritdoc}
     */
    public function is_hash_supported($hash)
    {
        return $this->get_password_hasher($hash) instanceof PasswordHasherInterface;
    }

    /**
     * {@inheritdoc}
     */
    public function is_hash_valid($hash)
    {
        $password_hasher = $this->get_password_hasher($hash);

        if (!$password_hasher instanceof PasswordHasherInterface) {
            return false;
        }

        return $password_hasher->is_hash_valid($hash);
    }

    // ...
}

The is_password_supported method is quite simple. We call the get_password_hasher method. We then check the returned value using the instanceof operator.

If the value returned by the get_password_hasher method is an instance of PasswordHasherInterface interface object, we return true. That’s because we know the chain has a password hasher that supports the given hash. Otherwise, we return false because the get_password_hasher method returned null.

The is_hash_valid method is similar to the is_password_supported method. We start by getting the password hasher for the given hash value using the get_password_hasher method. We then use the instanceof operator again.

That’s where the similarities end. We have to do a bit more than just use the instanceof operator in the is_hash_valid method. We only use the instanceof operator to validate if the get_password_hasher method returned a password hasher.

If we don’t have password hasher, we want to return false since we have no way to validate the hash value. If we do have a password hasher, we want to run the hash through the is_hash_valid method of the password hasher. We then pass back the return the value from the password hasher as the return value from our is_hash_valid method.

Working with passwords

So far, we’ve only touched methods that dealt with the hash value. This is what we built the get_password_hasher method for. The two other methods of the PasswordHasherChain that we want to look at are for passwords.

/**
 * Manages a chain of password hashers.
 */
class PasswordHasherChain implements PasswordHasherInterface
{
    // ...

    /**
     * {@inheritdoc}
     */
    public function hash_password($password)
    {
        $hash = array_reduce($this->password_hashers, function ($hash, PasswordHasherInterface $password_hasher) use ($password) {
            if (empty($hash)) {
                $hash = $password_hasher->hash_password($password);
            }

            return $hash;
        });

        if (empty($hash)) {
            throw new \RuntimeException('Could not create a hash for the given password.');
        }

        return $hash;
    }

    // ...

    /**
     * {@inheritdoc}
     */
    public function is_password_valid($password, $hash)
    {
        return array_reduce($this->password_hashers, function ($check, PasswordHasherInterface $password_hasher) use ($password, $hash) {
            if (true !== $check) {
                $check = $password_hasher->is_password_valid($password, $hash);
            }

            return $check;
        }, false);
    }

    // ...
}

Above you can see these two methods which are hash_password and is_password_valid. Both methods are quite similar to one another. They rely on using the array_reduce function on the password_hashers array.

Both anonymous functions passed to the array_reduce function are just a guard clause. The guard clause is there to determine if we want to return the current carry value or not. If we haven’t, we get a new one from the current password_hasher.

For the hash_password method, we want to stop trying to hash a password once we successfully hashed one. We can test for that using the empty function. If the hash carry value is empty, we know we haven’t managed to hash the given password yet.

For the is_password_valid method, the guard clause doesn’t use the empty function. Instead, we only stop checking the password if check is identical to true. That’s because we want every password hasher to have an opportunity to check the password. If we stopped when check was false, we might omit a password hasher that could validate the password.

How to use the chain

So that’s it for the PasswordHasherChain class! We’re going to wrap this up by looking at how to use it with our other password hashers. Here’s how that looks:

if (!class_exists('PasswordHash')) {
    require_once ABSPATH . WPINC . '/class-phpass.php';
}

$password_hasher = new PasswordHasherChain([
    new NativePasswordHasher(),
    new WordPressPasswordHasher(new \PasswordHash(8, true)),
]);

The code above starts with a guard clause. We need it because WordPress doesn’t always load the file containing the PasswordHash class. So we need to check to see if it’s there first using the class_exists function. If the class doesn’t exist, we require the class-phpass.php file.

Once we’ve passed the guard clause, we initialize the PasswordHasherChain class. We pass it the array of password hashers which we initialize at the same time. The first one is NativePasswordHasher and the second one is the WordPressPasswordHasher.

The order of the password hashers in the array is important here. The chain will stop once it finds a valid password hasher to perform an action. So we want to order them so that our preferred password hasher gets selected.

For this scenario, we want the WordPressPasswordHasher to go last. We don’t want it to hash new passwords using the WordPress algorithm. We only want it validate passwords that were already hashed using that algorithm. (This is why the is_hash_valid method of WordPressPasswordHasher class returns false.)

Seeing it in action

To see how the chain works in a plugin, I suggest looking at the Passwords Evolved plugin code. (Reading code is a great way to learn too!) You’ll see how to replace the WordPress implementation with our own. There’s also a third password hasher just for libsodium.

If you’re just interested in the code, you can check out this gist.

Photo Credit: Vladyslav Cherkasenko

Creative Commons License