PHP password hashing and the new password_hash() function
Password hashing is a hot topic with many database breaches resulting in users passwords being revealed due to bad encryption techniques or even worse none at all! Never store a password in plain text, you’re not doing that anyway right?
- Don’t use MD5, Base64, SHA*** to hash passwords
- Use the built in password_hash()
- If password_hash() is not available in your old PHP version use bcrypt
So what’s the problem? Why do we need this new password_hash() function in PHP? Well I’m amazed it’s taken this long for something like this to be implemented. The last ‘wave’ of hashing algorithms came in the form of MD* family, SHA* family, RIPEMD* etc etc Now whilst these give you some level of security the very nature of these algorithms was speed. Speed is the devil when it comes to password hashing. The whole point of hasing a password is to make it slow to ‘dehash’ in other words ‘crack’ using brute force techniques. The other issue with such hashing techniques is they produce the same string if the same password is used by multiple users, this helps facilitate in rainbow table attacks by simply match the hash against a table of ‘stock’ hashes.
There where ways around this which involved complex methods which many new PHP developers where simply unaware of or incapable of implementing properly, these included functions and libraries such as PasswordLib, Bcrypt and PHPASS to name a few. It’s also worth mentioning here the weakness of poorly produced salts can reduce the integrity of the above functions that require them. So there needed to be a sound solution to solve these problems.
In steps password_hash(). The password_hash was first implemented in PHP 5.5 although there is a back port to version 5.3.7 which can be found here: https://github.com/ircmaxell/password_compat working alongside password_verify() PHP developers now have a solid password hashing mechanism with room for improved hashing algorithms in the future without any changes to the code.
What is it?
The password_hash() function by default uses the bcrypt algorithm designed by Niels Provos and David Mazieres which is a crtpy() C library scheme based on Blowfish. Why is this important? Well the C library of crypt() has been around for a long time and has stood the test of time very well when accompanied with a salt, there are also many new schemes for crypt() such as Blowfish, SHA2, NT Hash etc adding to its complexity.
The password_hash() function makes it super easy to generate a complex hash string which is also flexible according to your hardware set-up. By default the password_hash() function uses Bcrypt but you also have the option of crypt_blowfish as well. Whats good about this approach is as more hashing algorithms are produced in the future they can easily be added to the PHP core and the PHP core team can also change the ‘default’ option from Bcrypt to something more secure if needs be in the future without you having to worry about the latest attack techniques or make any changes to your code.
What does password_hash() produce?
The password_hash() in its current format as of version 5.5 produces a string containing 60 characters like so:
So this gives us a number of things, the first part of the string identifies the version of the algorithm used, in this case $2y$which indicates the password_hash() is uing the latest version of bcrypt which includes some bug fixes found back in 2011. The second section is the 10$ which is the ‘cost’ factor the password_hash() function has used to hash the password. This can be set by passing an options array in to the password_hash() function, you can read my tutorial on how to implement this if you are unsure. Choosing a correct cost factor for your current hardware set-up is important, I have written a tutorial if you are unsure what’s best for your hardware set-up.
The 3rd part of the string is 22 characters long, this is the salt the password_hash() has generated for you. You can pass your own salt using the options array for password_hash() but its highly recommended you don’t us this option and simply use password_hash() built in salt which is generated automatically for you. The password_hash() function uses the /dev/urandom file on unix systems to create the salt which is a pretty solid and tested way of generating a random string, for the unlucky people who are deploying on a Windows based server it will use CryptGenRandom() which also has solid foundations.
The final part of the string is the hash itself. This is the magic moment where your normal human readable (and sometimes not) password is converted into a random string and stored safely.