Hashing Passwords with the PHP 5.5 Password Hashing API

Sandeep Panda

Using bcrypt is the currently accepted best practice for hashing passwords, but a large number of developers still use older and weaker algorithms like MD5 and SHA1. Some developers don’t even use a salt while hashing. The new hashing API in PHP 5.5 aims to draw attention towards bcrypt while hiding its complexity. In this article I’ll cover the basics of using PHP’s new hashing API.

The new password hashing API exposes four simple functions:

  • password_hash() – used to hash the password.
  • password_verify() – used to verify a password against its hash.
  • password_needs_rehash() – used when a password needs to be rehashed.
  • password_get_info() – returns the name of the hashing algorithm and various options used while hashing.

password_hash()

Although the crypt() function is secure, it’s considered by many to be too complicated and prone to programmer error. Some developers then use a weak salt and weak algorithm for generating a hash instead, for example:

<?php
$hash = md5($password . $salt); // works, but dangerous

But the password_hash() function can simplify our lives and our code can be secure. When you need to hash a password, just feed it to the function and it will return the hash which you can store in your database.

<?php
$hash = password_hash($passwod, PASSWORD_DEFAULT);

That’s it! The first parameter is the password string that needs to be hashed and the second parameter specifies the algorithm that should be used for generating the hash.

The default algorithm is currently bcrypt, but a stronger algorithm may be added as the default later at some point in the future and may generate a larger string. If you are using PASSWORD_DEFAULT in your projects, be sure to store the hash in a column that’s capacity is beyond 60 characters. Setting the column size to 255 might be a good choice. You could also use PASSWORD_BCRYPT as the second parameter. In this case the result will always be 60 characters long.

The important thing here is that you don’t have to provide a salt value or a cost parameter. The new API will take care of all of that for you. And the salt is part of the hash, so you don’t have to store it separately. If you want to provide your own salt (or cost), you can do so by passing a third argument to the function, an array of options.

<?php
$options = [
    'salt' => custom_function_for_salt(), //write your own code to generate a suitable salt
    'cost' => 12 // the default cost is 10
];
$hash = password_hash($password, PASSWORD_DEFAULT, $options);

In this way, you are always up-to-date with new security measures. If PHP later decides to implement a more powerful hashing algorithm your code can take advantage of it.

password_verify()

Now that you have seen how to generate hashes with the new API, let’s see how to verify a password. Remember that you store the hashes in a database, but it’s the plain password that you get when a user logs in.
The password_verify() function takes a plain password and the hashed string as its two arguments. It returns true if the hash matches the specified password.

<?php
if (password_verify($password, $hash)) {
    // Success!
}
else {
    // Invalid credentials
}

Just remember that the salt is a part of the hashed password which is why we are not specifying it separately here.

password_needs_rehash()

What if you need to modify the salt and cost parameters for the hash strings? This is a concern as you might decide to improve security by adding a stronger salt or larger cost parameter. Moreover, PHP might change the default implementation of the hashing algorithm. In all of these cases, you would want to rehash the existing passwords.

password_needs_rehash()

helps checks if the specified hash implements a particular algorithm and uses specific options like cost and salt while being created.

<?php
if (password_needs_rehash($hash, PASSWORD_DEFAULT, ['cost' => 12])) {
    // the password needs to be rehashed as it was not generated with
    // the current default algorithm or not created with the cost
    // parameter 12
    $hash = password_hash($password, PASSWORD_DEFAULT, ['cost' => 12]);

    // don't forget to store the new hash!
}

Keep in mind that you’ll need to do this when the user tries to login to your website as this is only time you have access to the plain password.

password_get_info()

password_get_info() accepts a hash and returns an associative array of three elements:

  • algo – a constant that identifies a particular algorithm
  • algoName – the name of the algorithm used
  • options – various options used while generating the hash

Conclusion

The new password hashing API is definitely easier to work with than fumbling with the crypt() function. If your website is currently running on PHP 5.5, then I strongly recommended that you use the new hashing API. Those who are using PHP 5.3.7 (or later) can use a library called password_compat which emulates the API and automatically disables itself once the PHP version is upgraded to 5.5.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Anthony

    And you can add compatible functionality with this: https://github.com/Antnee/phpPasswordHashingLib

  • Anonymous

    Great article. Thanx!

  • Said Bakr

    There is illogical function: password_verify()! Why we did not do it as usual? i.e using password_hash() to hash the password entered by the user and comparing its output with the string stored in the database?!!! What is the value added with password_verify()?

    • Peter

      Said. Try hashoing the password numerous times, you will find the hash is different each time.

  • Anthony

    Said, it is because the function continues to work even if you change your hash algorithm. This is not the case with hashing again and comparing to the stored hash

  • julius

    sad that not all hosting providers have php5.5
    So we have to use
    $hash = sha1(md5($salt).time().md5(strrev($pwd)).$pwd);

    • Anthony

      Julius, these features effectively wrap existing PHP functions, such as crypt() so that is not the case at all. In fact, your suggestion is one I would recommend nobody use. sha1() and md5() and not designed for storing passwords. Look at the link in my first comment above and you can see how to add these features to older PHP versions

  • kAlmAcetA

    one only thing – “next new” API for OO language in functional manner… nonsense

  • Anonymous

    If you need a ready-to-go fully-implemented solution that uses these function but also has full fallback support for PHP 5.3 and PHP 5.4, check out http.//www.php-login.net !

  • http://www.bitfalls.com/ Bruno Skvorc

    Note that you can now add this functionality to 5.3.7+ with https://github.com/ircmaxell/password_compat