Hashing Passwords with the PHP 5.5 Password Hashing API

    Sandeep Panda
    Share

    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($password, 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.