I got a call from a client with a membership site that’s been around since the dawn of time. They wanted to upgrade their security, specifically their WordPress password hashing. The problem? They had users whose passwords were still hashed with the old PHPass library, maybe even some MD5 if you dug deep enough. They needed to transparently upgrade any user who logged in to a modern algorithm like Argon2, but without invalidating any of the old passwords. A real mess.
My first thought was to just hack something into the wp_check_password function. You know, a big if...elseif block. Check for Argon2 support, then check for bcrypt, then fall back to the default WordPress hasher. It would have worked, kinda. But it felt dirty and brittle. What happens in two years when there’s a new algorithm? I’d have to go back and add another `elseif`. Total nightmare to maintain.
A Better Approach to WordPress Password Hashing
This is a classic scenario for a design pattern called the “Chain of Responsibility.” It sounds more complicated than it is. Trust me on this. The idea is to create a chain of handler objects. When a request comes in—in our case, a password to verify—you pass it to the first handler. If it can deal with it, great. If not, it passes the request down the line to the next handler, and so on, until one of them handles it.
It’s the object-oriented version of that messy if...elseif block, but clean and extensible. This approach is super flexible. To support a new hashing algorithm, you just write a new handler class and add it to the chain. No need to touch the existing logic. This is the exact pattern I used in my open-source plugin, as detailed in a post over at carlalexander.ca, which inspired this approach.
The whole thing starts with a simple interface. Every handler in the chain has to agree to the same contract.
<?php
interface PasswordHasherInterface
{
public function hash_password($password);
public function is_hash_supported($hash);
public function is_password_valid($password, $hash);
}
Each class that implements this will handle one specific hashing algorithm. The key is the is_hash_supported() method. A bcrypt handler would check if the hash starts with $2y$, an Argon2 handler would check for $argon2i$, and the default WordPress hasher would look for $P$. When you need to check a password, you loop through your chain of hashers and the first one that returns true for is_hash_supported() gets the job of validating the password.
So, What’s the Point?
Writing code that’s easy to change is the mark of a senior dev. The Chain of Responsibility pattern is perfect for situations where you have a request and multiple possible handlers, but only one should act. In this password hashing scenario, it provides a clean, decoupled way to handle legacy, current, and future algorithms without creating a tangled mess.
- It’s Extensible: Need to add a new hashing function? Create a new class. Done.
- It’s Clean: Each class has one job and does it well. No monolithic function trying to do everything.
- It’s Readable: The logic is easy to follow. You have a chain, and you pass a value down it. Simple.
Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
The next time you’re faced with a series of checks, think about whether a Chain of Responsibility would be a better fit than a simple conditional. It might just save you a headache down the road.
Leave a Reply