As anyone whose used a web applications knows, the password is still the go-to form of identification. Sure, there have been lots of improvements in the world of identify over the last few years, but there’s still a constant flow of applications and websites that rely on this tried and true method of protection. Unfortunately, because it represents a single point of failure, it’s actually one of the least secure methods for providing your user is why they say they are.
There’s been a recent resurgence in alternate technologies to help protect your application’s users including a wide variety of two-factor solutions and things like federated identify providers. People are understanding more and more that a simple password isn’t enough. We see stories almost daily of some major company or group being hacked because of either bad passwords or bad password storage practices. Unfortunately, there’s only a limited amount of things you can do for the former (like more effective password policies), but there is a way to help with the second. It’s surprising to find out just how many companies and applications have made poor choices when it comes to how they protect their users’ passwords. There are even some that have made the disastrous choice to store them as plain text (it makes me cringe just thinking about it).
History of PHP Password Past
PHP-based applications have this same problem, just like any other. They’ve long suffered from both a lack of education of developers on the topic and a lacking toolset to more correctly protect these valuable pieces of information. Thankfully, in more recent versions of the language, there’s been more emphasis put on password security specifically. When you think about protecting passwords, one method is to take what the user gives you, salt it and then encrypt it and store that value. Depending on how it’s encrypted and how long it may take to decrypt, this could become a burden on your application. PHP has focused on an alternative that can be very effective at keeping your passwords safe – hashing.
Password hashing is a method that takes the user inputted password string and runs it through a hashing method and spits out a formatted string on the other side. Compared to encryption, hashing is quite a bit faster and is a one-way transformation. This means that once the hash is created, there’s no way to reverse it without quite a bit of work. In fact, the way to “crack” a hash usually involves a program that runs through all of the possible string combinations and hashes them looking for a match. Obviously, with a powerful enough computer, breaking a hash could be relatively trivial. Fortunately, PHP includes support for a hash type that helps slow this process down dramatically,
Getting into crypt
bcrypt to hash your passwords provides a few added values. First, it’s a more complex hashing method that iterates over the generated hashes rather than just a single execution (like a call to
sha1() would produce). It also provides a more complex hash than you’d get with some of the older methods. It creates a predictable length string that can easily be stored in a database or some other user-related data store. Getting back to PHP, though, as of PHP version 5.5.0 there’s a wrapper that makes it much simpler for developers to more correctly hash and validate their users’ passwords – the password_hash method and its relatives. This handful of methods makes it dead simple to implement. Essentially it’s a wrapper around the long-time crypt functionality, but with a simplified purpose.
Here’s an example of it in use, salting and hashing a given password:
$password = 'sup3r53cr3t!';
$options = [
'salt' => mcrypt_create_iv(22, MCRYPT_DEV_URANDOM)
$hash = password_hash($password, PASSWORD_DEFAULT, $options);
In the code above we’re taking the users
$password and using the default hashing method (right now it’s
bcrypt but that could change in the future) and returning the hash. I’ve opted to manually provide a
salt value in this call, but if you don’t it will all be handled internal to the function. In most cases, you’re good with letting the function take care of that itself, but the option in there.
The result of the
password_hash call is something like this:
$2y$ identifier lets you know this hash is Blowfish-based.
Remember what I said about
crypt and how it iterates over the value and hashes multiple times before it finally returns the finished product? This is called the “cost” of the hashing and the
password_hash function lets you set this value too. The higher the value, the more iterations it goes through to produce your hash so be careful with how high of a value you use. Here’s sample code showing the setting of the “cost”:
$options = [ 'cost' => 12];
$hash = password_hash($password);
In this example I’m setting the cost up to 12, meaning it will run through 12 iterations before getting an end result. The
password_hash functionality defaults to a cost of 8 which seems to be a good balance between performance overhead and “time to crack” for most applications. If you have a keen eye, you’ll also notice that the cost you hashed with is included in the result (inside the second set of
Now, for the other side of things, PHP (again, 5.5.x) also offers something to validate the hash it’s been given quickly and easily – the
password_verify function. This function, given a hash you’ve already stored and the password string from the user can return a
false as to whether they match up.
No silver bullets here
Just like with anything else in security, there’s no such thing as a silver bullet, and this password hashing functionality is no different. While salting and hashing your passwords can help prevent easy access of them by attackers, given enough time, they can and will break them. This is where other password-related technologies come in – things like password policies, expirations and resets. If you make effective use of these three things, you users should be cycling passwords that are stronger and more resilient to cracking every so often. This greatly reduces the chance of a compromise through a cracked password. Using something like
bcrypt makes the cracking process go slower and may even take longer than your password cycling policy (which means the attacker, even with an offline database is out of luck).
Don’t forget that password storage is only half the battle – you still have to help protect your users from themselves. Make effective use of other password handling techniques and maybe even introduce things like two-factor authentication to prevent even more issues.