Killing rainbows

By Daniel Mason | 18 October 2013 | Comment


You often read about user password details being stolen from websites and other services, but how can you make sure your users' passwords are kept safe?

Hashing passwords is pretty standard. You don't keep your users passwords in plain text because if you get hacked, the attacker can just see their password, then use it to log into your site or worse, other peoples sites. Additionally the password hash can't be turned back into a actual password... or can it?

Enter Rainbow Tables

A rainbow table is a huge list with two fields, the potential password and the hash of that password. If you hash all of your passwords in the same way, even if that way looks pretty clever, then your user data can be cracked open by a rainbow table.

The absolute worst thing you can do is this:
$passwordHash = md5($password);

All an attacker has to do is hash a whole load of common words (followed up with a lot of random strings) in the place of the password to find matching hashes. The kicker here is that the password they use doesn't have to be the same as the one the user used, it only has to create the same hash. If the. Worse yet, if the attacker did stumble onto the actual password, they can then log into other websites using that users credentials. It will still work if they didn't get the actual password if the next site uses the same hashing method. You've not only compromised your own sites security but others too!

If you're a little more sensible you can hash your password like this:

$passwordHash = sha1($secretSiteKey.$password);

This is fine, as long as the attacker never gets hold of the your site's secret key. If they do they can make a new table based on this information and you have the same problem as before.

So how do we kill a rainbow? With salt.

Salting passwords

Salts are different to keys because salts change every time, they add flavour, get it?

In the example with the key above, a new rainbow table had to be created taking the key into account. Using a salt which is different for every user means a new rainbow table must be created for every user. Rainbow tables are huge, containing hundreds of thousands of entries: it takes time to make them. If you have a thousand users, the attacker must make 1000 rainbow tables, each an amalgamation of hundreds of thousands of hashes.

For one last twist of the dagger, we can re-salt our hashes a large number of times, lets say 1000. Now the attacker needs to perform the hash on each word 1000 times, and they'll need to do it hundreds of thousands of times to get a good table and they have to do it for every user. Suddenly it's no longer worth the hassle.

Caution: When you hash a hash multiple times, you are hashing strings of equal length, some hash algorithms, like md5, have a high rate of collisions among equal length strings.

Example

/**
* Hash the given password
* @param $password
* @return string
*/
public function hashPassword($password) {
global $config;
if(!$this->salt)
$this->salt = Tools::randomAscii(32);
$siteKey = $config->getSiteKey();
$hashedPassword = $password;
$salt = $this->salt;
$nHashes = 1000;
for($i = 0; $i < $nHashes; $i++)
$hashedPassword = hash_hmac('sha256', $hashedPassword.$salt, $siteKey);
return $hashedPassword;
}
comments powered by Disqus