Safe(r) Passwords in PHP

Using bycrpt for safer password storage

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, bcrypt.

Getting into crypt

Using 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 md5() or 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:

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$11$q5MkhSBtlsJcNEVsYh64a.aCluzHnGog7TQAKVmQwO9C8xb.t89F. The $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”:

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 $ characters).

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 true or 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.

Related

Sign up for the O'Reilly Programming Newsletter to get weekly insight from industry insiders.
topic: Programming
  • http://josephscott.org/ Joseph Scott

    a lacking toolset to more correctly protect these valuable pieces of information.

    phpass? http://www.openwall.com/phpass/

    It is at least 8 years old and has been widely deployed. If you are using newer versions of PHP definitely use password_hash(). But for older versions of PHP there has been a reasonable library available for a long time.

    • ccornutt

      Very true, it’s a good standby that’s been around for a while…it hasn’t gotten much press in that time, so I’m not sure how many devs out there know about it. I was more going for the “inherent to PHP” approach with this feature, though. Depending on company policy, external libraries like phpass may not be able to be used where something already built into PHP may be.

      • http://josephscott.org/ Joseph Scott

        The phpass library is fairly small, even if you had a policy that forbid outside libraries (which seems like over kill, do they review the source code to native PHP functions too?) the code is short enough to read over and figure out how it works. Re-implementing it wouldn’t be hard.

        I like that newer versions of PHP will have an easy way to hash passwords ( though the API signature is strange ), just don’t discount that you’ve been able to do it properly in older versions of PHP for some time.

        • ccornutt

          I’ve worked at places before that required all 3rd party code to go through a security audit. We never used phpass specifically, but I could see there being things in it that might trip up some paranoid auditors. I am glad that someone’s taken it and made it easy to install via Composer too: https://github.com/hautelook/phpass

  • Ray Paseur

    Password security is something like a fire safe — rated on average survival time before the contents are incinerated. And you just can’t fix stupid, like the poor bloke who chooses “password.” I’m becoming a fan of Pass-Phrases. Suitably salted, they might offer a little more resistance to cracking.

    • ccornutt

      True, but you can use things like password policies to help protect from that. While I’m not a fan of password policies that limit what the user can do, I do think that ones that say “must include at least…” can help prevent issues.

  • ezimuel

    It’s a pity that the post didn’t mention
    ZendCryptPasswordBcrypt, a component of ZF2 that offers a simple API for
    bcrypt since PHP 5.3.3. Here more details: http://framework.zend.com/manual/2.2/en/modules/zend.crypt.password.html#bcrypt

  • http://www.acewebacademy.com/ Acewebacademy

    I too agree when saying the password policies should not limit what a user can do. However the safety issue also to be taken care of.

  • Brandon Savage

    Don’t you have to pass $options to the password_hash() function as the third argument (last code sample).