Codular

HomeWriters RSS

PHP Password Hashing

Introduction

Previously we've discussed how you can go about storing your passwords if you're using PHP, and the best way to hash them.

However, with more recent versions of PHP (5.5+), there are some actual methods included in the core language that you should use instead. These will take out a lot of the trouble that you might have working out the best way to create a salt, or how to ensure that you're doing it correctly. Best of all, it's native too - so if you're able to I suggest you switch to using this method anywhere/everywhere you generate passwords.

The great benefit is, because our previous example used BlowFish, you can easily switch to using BlowFish with the native methods with complete ease.

Hashing a Password

The hashing logic is as simple as doing this:

password_hash('MySuperSecureSecr37p455w0rd!', PASSWORD_BCRYPT);

We pass in the string that we want to hash, and the method of hashing that we're looking to use. You can omit the second parameter, and stick to the default that PHP chooses to the best. However, a future version of PHP might change your actual hashing method. If you're looking to run with the best suggested default hash, then read below to ensure you use the password_needs_rehash() method to check if you need to resave a user's password.

By default the method will use a cost of 10, which is a good level to start at, however if you want to change this, you can pass through the cost as a third parameter as an array of options. The below example sets the cost value to 15 (min is 4, max is 31)

password_hash('MySuperSecureSecr37p455w0rd!', PASSWORD_BCRYPT, ['cost' => 15]);

It's just that easy.

Checking a Password is Valid

To check that a password is valid, you use a different method, but much like the older crypt() example you still use the hash that you have stored, and the password - this will return a boolean:

$currentPassword = '$2a$15$Ku2hb./9aA71tPo/E015h.LsNjXrZe8pyRwXOCpSnGb0nPZuxeZP2';
$checkPassword = 'passwords';

if (password_verify($checkPassword, $currentPassword)) {
    echo 'You are in!';
} else {
    echo 'You entered the wrong password';
}

Nothing complicated here at all, there's no reason to not be doing it!.

Remember: - Never store your password in plain text - ever! - You will need to ensure that the host you have is on PHP 5.5 or above.

Using Default Hash Method

One thing that the PHP 5.5 native password implementation allows you to do is to use the default hashing method. This (currently) is BlowFish, but might change in the future if there is a stronger algorithm available. If the algorithm changes, and you're using PASSWORD_DEFAULT as the alogrithm in the above code your passwords might remain being stored in the older algorithm.

Therefore, when a user logs in, it is worth checking to see if the password needs to be rehashed to the newer updated hash algorithm. To do this, we use the method password_needs_rehash() which will return a boolean. We can then use this to regenerate a hash, and resave that to the database.

Remember, you should only rehash if the user logs in successfully, as we need their password to generate a new hash - which we won't otherwise have. Here's the example code from the PHP Site:

$hash = '$2y$10$I0ah96.G2.y13n6m0.ENJOraS9l1RpuRsJdK8JLy3F6EQHxeMKJl6';
$password = 'testing';

// Change the cost of passwords being stored
$options = ['cost' => 20];

if (password_verify($password, $hash)) {
    // Check if the password needs to be rehashed
    if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
        // If so, create a new hash, and replace the old one
        $newHash = password_hash($password, PASSWORD_DEFAULT, $options);
    }
    // Log user in
}

**Note: ** If you change the cost of the hash (like we have above), this rehash method will trigger true, and force a new hash to be saved. So if you feel like changing your cost parameter at a random interval, you can do and your application will work the same, but your users will potentially be more secure!

Further Considerations

While a high cost parameter will increase the time it takes to brute force passwords, you should make sure that you implement a method by which you either lock down a user's account after a set number of invalid login attempts, or require them to wait a set period of time before continuing further. The method you use is up to you, but some suggestions:

If in the unfortunate case your hashed passwords get out, you are completely safe because each password will have to be bruteforced individually if they all use a different unique salt. In which time, you should be more than able to notify your users, and change all of their passwords, and increase your cost parameter.

Finally, if you're sending your user a method by which to reset their password, it should be impossible for you to send them the plain text version - chances are that if a site either limits the length of the password, or can email the password to you in plain text, it's storing them incorrectly.