# salted and hashed passwords, using PHP and MySQLi

### what is a salt?

In cybersecurity, a salt is an extra layer of protection to prevent attacks from rainbow tables. A rainbow table is a precomputed table for reversing hash functions. For instance, take an example database, consisting of usernames and hashed passwords (hashed with the weak and easily-cracked MD5 ) and with the cleartext password for demonstration purposes:

Notice how administrator and just_a_guy have the same password hash? Now look at a sample rainbow table for MD5 :

Hash Cleartext
202cb962ac59075b964b07152d234b70 123

A rainbow table will look for similarities, and will replace the hashes with cleartext

### how can we protect against this?

I’m glad you asked! Obviously, to improve our defense, we can do a few things. Namely, don’t use MD5 , use a secure hashing algorithm, like Argon2 . However, we can take this one step further. What if we add a random number to the hash as well? This is called a salt. It is not a secret number. It is often stored alongside the hash, or even in it’s own column. The point of a salt is to make it more difficult for a rainbow table attack to work. Since we add a salt to the hash, a rainbow table attack will no longer improve efficiency for the attacker (theoretically speaking, this increases the worst case scenario of a rainbow table attack from O(n) to O(n * m), where n = # of password guesses & m = # of database entries .

Consider our new and improved table, now with a salt appended to the end of the password before hashing (for this example, I’ll be using a 4 digit number off random.org ):

2 john123 3d2307bf44bd1da1d8bb401a208a8497 4667 123456

Our hashes are completely different because of the salt, even with the same password! Now, if an attacker wants to use a rainbow table, they have to take in to account every single possible salt along with each password. In this example, with a salt of only 4 integers, a rainbow tables size would have to increase by around 1000 times! Imagine doing that for each password with a salt the same length of the hash (and with letters and symbols as well as numbers)!

### writing it

I’ll be using PHP’s built-in function md5() for this because I don’t feel like implementing it myself.

For the encryption:

<?php
// connection to the server or die with the error
$connection = mysqli_connect('localhost', 'user', 'password', 'table') || die(mysqli_connect_error()); // get username and password (i'm assuming we're using POST)$username = $_POST['username'];$password = $_POST['password']; // get a salt of 4 integers (should be random, i'm copying a function from openwall.com)$salt = random_bits(4);
// get the md5 of the password and salt
$hash = md5($password . $salt); // alternatively, you can use a version where the salt is stored along with the salted hash: //$hash = $salt . md5($hash . $salt); // now set up the SQL and execute$sql_result = mysqli_query("INSERT INTO users (username, hash, salt) VALUES ('$username', '$hash', $salt)"); // for the alternative hash: //$sql_result = mysqli_query("INSERT INTO users (username, hash) VALUES ('$username', '$hash')");

/**
* generate pseudo random bits
* copied from
* http://www.openwall.com/phpass/
*/
function random_bits($entropy) {$entropy /= 8;
$state = uniqid();$str = '';
for ($i = 0;$i < $entropy;$i += 16) {
$state = md5(microtime().$state);
$str .= md5($state, true);
}
$str = unpack('H*', substr($str, 0, $entropy)); // for some weird reason, on some machines 32 bits binary data comes out as 65! hex characters!? // so, added the substr return substr(str_pad($str[1], $entropy*2, '0'), 0,$entropy*2);
}
?>



And to decrypt:

<?php
// connection to the server or die with the error
$connection = mysqli_connect('localhost', 'user', 'password', 'table') || die(mysqli_connect_error()); // get username and password (i'm assuming we're using POST)$username = $_POST['username'];$password = $_POST['password']; // get current hash in database$sql_result = mysqli_query("SELECT hash, salt FROM users WHERE username='$username'"; // for alternative hash: //$sql_result = mysqli_query("SELECT hash FROM users WHERE username='$username'";$rows = mysqli_fetch_row($sql_result); // now check against the salt if (md5($password . $rows[1]) ==$rows[0]) {
// success!
} else {
}
// for the alternative hash:
/* $hash = substr($rows[0], 4);
$salt = substr($rows[0], 0, 4));
if ($salt . md5($hash . $salt) ==$rows[0]) {
// success
} else {


Don’t. Use PHP’s built-in functions password_hash() and password_verify() for password hashing. The hash generated by password_hash() contains the salt and algorithm used built in. As an added bonus, if you’re going to use your own implementation, or just not PHP, you can use a uniquely generated salt (stored in the database) along with a fixed salt stored in a file seperate from the database. This requires access to both the filesystem and the database.