Codular

HomeWriters RSS

Storing Passwords with PHP

Introduction

Chances are that you have a password to something, and that password is probably also stored somewhere. Chances are, if you're a web developer, you've probably wondered how best to store a password for your users to later use when logging into your website. Plain text? MD5? SHA1? Bacon substituted (this doesn't exist)?. All of these are foolish ways to store a password, especially the last one, I don't even know what that is. You should be using bcrypt to store your passwords, if you're not - well you should! At least reassure me that you're using a salt!?

If you are using PHP 5.5 or higher, it is strongly advised that you take a look at using the more secure, native implmentation of the below code. We have a handy tutorial available for you to view: Hashing Passwords in PHP 5.5

Let's Go!

To do this, we'll be using the crypt() method with a unique salt. Now many times people think that a salt needs to be unique for that user, which is true in a lot of cases. But with bcrypt we can use a salt that is unique to that password. The salt that is used, will end up being returned within the hashed password - so no need to separately store the salt.

Generating a Salt

If you don't know what a salt is, it's a random string that is added during hashing, and used to make that hash even more obscure. For a salt we can use something like this:

$2a$15$abcdefghijklmnopqrstuv$

The issue with that is the salt isn't unique, random or unguessable. The letters that are used within the salt are 22 characters from the collection of: ./a-zA-Z0-9. Ideally we should use a random function, which could use either a microtime of the moment, or even some other crazy method! Here's a more ideal salt:

$2a$15$Ku2hb./9aA71tPo/E015h.$

Let's just quickly break down the salt for you - it's ultimately in three parts, one of which I've already mentioned. The $2a$ section is an identifier to let the PHP method know that we are using the BlowFish hashing algorithm. The second part, the 15 section is the cost parameter. This is in the range of 4-31, and is the iteration count for the algorithm - very basically it will make it so that bruteforce attacks take longer, if that number is higher. The higher this number, the longer the password takes to be generated

Now that we have the salt, let's hash a password.

Hashing a Password

The hashing logic is as simple as doing this:

crypt('password', '$2a$15$Ku2hb./9aA71tPo/E015h.$');

We pass in the string that we want to hash, and the salt - which includes the instruction to use the Blowfish algorithm. Some example words with their returned hashes:

password: $2a$15$Ku2hb./9aA71tPo/E015h.LsNjXrZe8pyRwXOCpSnGb0nPZuxeZP2
password2: $2a$15$Ku2hb./9aA71tPo/E015h.CNGqVsxZZBYTC/r1Os396YragLJGV.W
mypassword: $2a$15$Ku2hb./9aA71tPo/E015h.h9vBlPsdHlKzNVtKICiGuyZ8A.1ejiy
1234: $2a$15$Ku2hb./9aA71tPo/E015h.8Tj1dOqu1OC3tr87Tke2Ef0zrLZ5ooa
pass123: $2a$15$Ku2hb./9aA71tPo/E015h.GXUYuw0uJWtFqjpWvgTPoPFOQV09.rG

It's just that easy.

Checking a Password is Valid

To check that a password is valid, you simply use the same method, but for the salt pass in the hashed password that you have stored in the database as follows:

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

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

Nothing complicated here at all.

Remember

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.