Storing passwords with Bcrypt [PHP]

It’s always a hard decision how one should store a password in PHP. Mostly you would stick with md5 or sha1, but there’s a big problem with them: speed. They are designed to quickly hash large amounts of data, so you can e.g. test if a big file was downloaded correctly. So that wouldn’t be a good way to store your passwords, because they can be cracked easily if your database got hacked.

I ran a short test, it takes my iMac only ~1.3 seconds to hash 1,000,000 passwords with md5, for sha1 it’s ~1.4 seconds. So to crack a weak, 6 character long password with a brute-force attack, it takes me at most 15 minutes. That’s quite bad, hugh? Therefore, we have to use an algorithm that is much more complex. Bcrypt is designed with exactly that problem in mind, and it’s quite simple to use with PHP.

To make it even more secure, we also use a new salt for every password, so that an attacker can’t access two accounts with the same password if he hacked one. The code is very simple:

function passEncode( $pass ) {
	$salt = '$2a$08$' . md5(microtime()) . '$';

	return crypt($pass, $salt);
}

The salt consists of three parts, separated by the $ sign. The “2a” tells the crypt function that we want to use Bcrypt, then follows a two-digit cost between 4 and 31. It indicates how long it takes to encrypt one password, e.g. if we use 11 instead of 8, it takes 2^3 = 6 times longer. The last part is the real salt, we just use a hashed microtime here because it can only be 22 characters long.

The more tricky part is to test if a password matches a given hash. To accomplish that, we first look what the crypt function returns:

$salt = '$2a$08$' . md5(microtime()) . '$';
echo $salt."\n";
echo crypt('test string', $salt);

// That leads to the following output:
// $2a$08$94b0f7a41348fdbe5e1d6c46ec178528$
// $2a$08$94b0f7a41348fdbe5e1d6OTO/ChRgYosYzriQ7TwU23qVeEDPeYcK

As you can see, the output starts with our salt already, so there’s no need to save it anywhere else. Now to test the password, we just hand crypt the password we want to test and the complete hash from the database. It gets cut off at the right position automatically, so our check function is:

function passCheck( $pass, $hash ) {
	return crypt($pass, $hash) === $hash;
}

And we’re done!

Hint: Of course the code above is ready to be copy-pasted into your project!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>