The Blowfish Debacle

With the release of PHP 5.3.7 a small change was made to upgrade crypt_blowfish (for crypt()) to version 1.2. This was a great security fix, solving an issue with insecure passwords due to incorrect behavior.

However, what wasn’t made clear, is that this change was actually a backwards compatibility break. If you upgraded to 5.3.7+ data hashed pre-5.3.7 would no longer match data hashed post-5.3.7; this means if you use it for passwords, it will no longer match.

So what’s the deal here?

To encrypt using blowfish in pre-5.3.7, you would prefix your salt with $2a$ followed by a 2 digit cost parameter (base-2 logarithm, between 04-31), another $ sign, and finally 22 alphanumeric characters (0-9, A-Z, a-z).

So you end up with something like this: $2a$10$22randomcharactershere$

With PHP 5.3.7, the behavior of crypt() when using the $2a$ has changed to promote the new, more secure behavior — this is where the backwards compatibility break comes in. Now $2a$ will use the correct behavior, and has explicit countermeasures against the insecurities of the old behavior.

So what do you do when you’ve got these already hashed passwords? Reset all your user passwords? That suck!

Luckily, two new prefixes were introduced, $2x$, which exactly replicates the behavior of $2a$ in pre-5.3.7 and $2y$ which is secure like the new $2a$ but with no countermeasures.

What this means is, you can simply do the following to upgrade your passwords on-the-fly:

Now, this code assumes that you spotted the issue before updating the server; if you have had new passwords created since the upgrade, you will want to check against the $2a$ hash if the $2x$ check fails, and then update to $2y$.

The reason I chose to use $2y$ is that you can identify non-upgraded passwords by the old $2a$ prefix. I would recommend that after say, 3 months, you might want to examine your dataset for those still using $2a$ and then think about forcing password changes.

While re-using the same password might not be as secure as we would like, this ensures minimal user impact, and an easy upgrade path.

For better security, rather than automatically upgrading the hashes, you could check for $2a$ and require a new password be supplied before continuing.