Never store a users password as plaintext, ever.

Have I missed something? Am I wrong somewhere? Head over to the contact page and let me know. All examples are shown in PHP 5.5.

So you're planning on storing user passwords somewhere? Then lets go over a few things you should know before you do, the main one is above and I'll say it again, never store your users passwords as plaintext. If you do you're just asking for trouble.

So what's the difference between hashing & encrypting?

Hashing a string is a one way function and so the hash cannot be converted back into the original string, encrypting a string on the other hand is a 2 way function and once the string has been encrypted it is possible to decrypt the encrypted string back to the original string as long as you know the key used in the encryption process.

Hashing a is often referred to as "encryption" but this is an incorrect statement.

What should you use?

The simple answer is that you should be hashing your users passwords with a random salt, a decent cost amount and then storing the salted hash. So what hashing algorithm should you choose? How do I go about implementing this? I'll discuss this later, for now I want to quickly explain why you should be hashing and NOT encrypting your users passwords.

The encryption key used for encrypting and decrypting passwords is the weak link and if this is ever compromised then so is every bit of data you've encrypted with that key.

For example, if an attacker manages to breach your website and dumps a copy of your database whilst also getting a hold of your encryption key, with very little effort, ALL of your users passwords are now blown. Consider that over half of UK adults use the same password to access internet sites [1] and depending on how popular your site "was", you may have just inadvertently compromised thousands of other accounts.

You should try to avoid doing that.

Hashing, how and why.

Hashing your passwords is a much better idea and will give you a salted hash to store but you still need to get the implementation correct. As of PHP 5.5 there is a neat API which provides an easy and secure way to implement this correctly and will provide you with a salted hash, you can find full documentation of the API here.

An example of a basic password_hash & password_verify sequence can be seen below.


<?php

$hash = password_hash("sample_password", PASSWORD_DEFAULT);

if (password_verify("sample_password", $hash)) {
    echo "Valid password";
} else {
    echo "Invalid password";
}

?>

The above is a basic usage example of the password_hash function which uses the BCRYPT algorithm by default, there are other things to consider though when using password_hash such as the algorithmic cost and whether to use your own salt. Although password_hash will allow you to use your own salt I highly recommend that you don't do this, let password_hash provide the secure salt for you, in fact in PHP 7 the option to add your own salt has been removed.

The cost is highly dependent on your circumstances and hardware, if no cost is provided then a default of 10 will be used, it is suggested that you should increase this if your circumstances allow for it.

More information on password_hash is available here and I highly recommended that you read it.

All of the above sounds like a hassle, I'll just use md5() instead.

Don't ever use md5, sha1, sha256 or other fast hash functions for hashing your passwords, stick with either BCRYPT or PBKDF2

md5 and a lot of other hash algorithms are now considered dead in terms of cryptographic hashing because of how quickly modern computers/clusters can brute force them. md5, sha1 and other algorithms are designed to be very efficient and fast, allowing current hardware to brute force billions of hashes a second.

Algorithms such as BCRYPT and PBKDF2 are known as slow algorithms, which is good in terms of security as it makes a brute force attack less likely to succeed due to the greater cost and time scale involved.

To demonstrate what I mean by a fast hash and a slow hash I've put together a couple of tests to show you the difference in speed.

Each test was run on an Intel Core i3 2.3 GHz, keep in mind this is very slow when compared to a cluster of high end GPUs capable of far greater speeds.


// md5
Total hashes generated: 1,000,000
Total time taken: 8.00 seconds.

// sha1
Total hashes generated: 1,000,000
Total time taken: 9.57 seconds.

// sha256
Total hashes generated: 1,000,000
Total time taken: 16.79 seconds.

// gost
Total hashes generated: 1,000,000
Total time taken: 19.23 seconds.

// bcrypt with 10 rounds and random salt
Total hashes generated: 1000
Total time taken: 115.7 seconds.

Useful Resources

Quotes

  1. http://media.ofcom.org.uk/news/2013/uk-adults-taking-online-password-security-risks/