In this article I will explain the method I suggest for hashing password and saving them in a database.

First of all, never insert passwords to the database as plain text!
If your database is stolen your users will pay the price (maybe you will have to pay them for the damage you have caused).

The easy and common solution for this particular problem is to use an one-way hash function, such as md5 and sha1, which takes the password and encrypts it.
Unfortunately, this method is not as strong as you may think.
Nowadays hackers can use rainbow tables to reverse almost any password hashed with these algorithms in no time.
True, it takes a couple of hours to create an extensive rainbow table; however it takes a couple of minutes or even less to crack almost any password once the rainbow table is ready!
Moreover, there are many other attacks that can crack these hashes pretty fast.

To understand how you can protect yourself from rainbow tables and many other attacks using salts and a better algorithm, you should first understand what a rainbow table is.
Basically, a rainbow table is just a collection of all possible passwords (or all probable password) encrypted and stored in a way that is optimal for reversing hashes.
If we have to hash one password every few seconds or even less, the hacker has to hash every possible combination (for a 10 digits a-z and 0-9 password there are 3710 [about 4.8x1015] different combinations) as fast as possible, which takes them a couple of hours for algorithms such as md5 and sha1.

We can protect our password from rainbow tables and similar attacks by using salts.
A salt is an unique (and usually random) string that is added to every password before it is encrypted. It is worth mentioning that the salt doesn’t have to be highly random, but it must be different for each password.
If our salts are long enough, using rainbow table is impractical, even if the hacker knows both the hash and salt of each password.
The hacker can either create a different rainbow table for each password, which takes a few hours for each password and therefore impossible, or create a huge rainbow table, which will take hundreds of years to be created on modern computers and increase the look up time to hours rather than minutes.

We can, and should, make it even harder for the hacker.
Rather than hashing the password once, we can hash it thousands of times, with an algorithm that takes longer to run, for example sha512.
We will not feel the difference as our server barely hashes password, however the hacker has so many passwords to encrypt, no matters what hacking method he chooses.
For example, if we chose to use sha512 10,000 times rather than sha1, each password will take about 15,000 times longer to be created, about 30 password / second instead of 500,000 password / second!
So if it took the hackers a couple of hours to generate a rainbow table for sha1 (say 3 hours), it will take them about 5 years to generate a rainbow table for 10,000 runs of sha512!
Slowing the algorithm makes different brute force attacks, including rainbow tables, extremely inefficient thus impossible to use.

To sum the theoretical part of this article up, combining these two methods will protect your users’ password much better than regular md5 or sha1 hash.
Implementing this two ideas is not as hard as you may think, and here is the explanation for PHP.

Luckily PHP provides a function that does almost everything for us, the function crypt.
This function can encrypt passwords with few popular algorithms, I will focus sha512. If we use sha512 the salt should be 16 characters from the alphabet “./0-9A-Za-z”, a longer salt will be trimmed.

The following code will hash the password “not_a_real_password” with the salt “too_long_salt_is_used123″, using sha512 ($6$) 10,000 times (rounds=10000$):

echo crypt('not_a_real_password', '$6$rounds=10000$too_long_salt_is_used123$');
// output:
// $6$rounds=10000$too_long_salt_is$ewdGWdAWWX.x4lR7JZaeQTzAVOlZ70li8fu7MGFaIvN6sQ9J36l9sV5OKOGbMU4yut2RkjZhyQC0jg6HT3gFF.0.091614961624146

As you can see the algorithm indicator, the number of rounds and the salt are returned with the hashed value (even though the salt was trimmed to 16 characters).
You should store that result right in the database without changing it, cracking this data is almost impossible.

What we now need is a simple function to create a random salt:

function generate_salt() {
	$dictionary = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
	$dictionary_size = strlen($dictionary);
	$salt = '';
	for($i = 0; $i < 16; $i++) {
		$salt .= $dictionary[mt_rand(0, $dictionary_size - 1)];
	return $salt;

Now, assuming we have a variable named $password and we want to hash it we simply call:

crypt($password, '$6$rounds=10000$'.generate_salt().'$');

Assuming we have a variable named $password and a hashed password named $hash and we want to compare them we simply call:

crypt($password, $hash) === $hash;

This works since crypt uses the algorithm, number of rounds and salt that is specified in the hash, thus will return the hashed value as expected.

How easy was that?

I hope that I managed to show you the advantages of strong hashing method (or disadvantages of weak hashing method), and that you’ve learned something new.
Thanks for reading!

Tagged with:

14 Responses to Hashing Passwords Properly

  1. Mytskine says:

    Thanks for this article. Please note that crypt() has SHA12 algorithm only from 5.3.2 onwards. Rainbow tables aren’t lists of possible passwords (more like equivalence classes of passwords), but that doesn’t change the way to counter them.

    The method you promote here already has a portable and well-known implementation: PHPass.
    I think you should promote its use rather than home-made code.

    Usage of this library is extremly simple.

    require 'PasswordHash.php';

    // create password
    $hasher = new PasswordHash(8, FALSE);
    $hashedPassword = $hasher->HashPassword($plainPassword)

    // validate
    $hasher = new PasswordHash(8, FALSE);
    if ($hasher->checkPassword($plainPassword, $hashedPassword)) {

    Understand how a library works is useful, but one shouldn’t rewrite it unless necessary. Especially for security libraries. For instance, I believe that PHPass is right when it says that using MD5 or SHA-256 doesn’t matter much, it’s the strechting that makes a difference.

  2. Naren says:

    Brilliant post. After reading this one, I went back and read the three previous posts, and I really enjoyed the NoORM/SplFixedArray posts as well.

    Keep writing. I’ll keep reading :)

  3. Joseph Scott says:

    For password hashing in PHP I point people to phpass – – which takes care of salting and stretching for you.

    • Thank you for the reply.
      PHPass was already mentioned by Mytskine, and I gree that it is a good solution.
      That said, I think that it is essential to understand the theory as well.

  4. sgoodkin says:

    Collection of some of your personal information is essential for completion of some of the functions and activities of this Website. We will

  5. Onno Marsman says:

    I am not entirely sure, but doesn’t applying a hashing algorithm 10.000 times increase the chance of a collision a lot? This could possibly result in multiple passwords to be accepted. I know this is true for hash(hash($password)) and I think it is also true for hash(hash($password.$salt).$salt).

    • As much as I know it does not increase the change of collisions, but I did not read enough about this issue.
      That said, no collisions were found in sha2 family.

  6. Fred Lieu says:

    Cool, there are some great tips on here that my clients may find this relevant, I’ll email them a link. Thanks

  7. Mark says:

    Check this out
    He got to 2.5B passwords par second with MD5, using a 1000$ price hardware…

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Set your Twitter account name in your settings to use the TwitterBar Section.