[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More


Salting Password Hashes

Published on
10,920 Points
2 Endorsements
Last Modified:
Community Pick
passwordbox.pngSeveral web applications today still use a weak password encryption. Passwords are secret information that should never be seen. That’s one of the reasons why dots or asterisks appear when entering passwords.  

Many websites take the precaution of using secure connections so password never travels across the wire in plain text.  Also, most web applications today take security measures to ensure passwords cannot be seen in plain text once it is stored in the server. Most applications encrypt passwords using one-way encryption. This is also the reason why when we forget our password, we only have an option to reset it. One-way encryption works like this:
User registers to the website, supplies password.
Site receives the password, encrypts the password using a one-way encryption algorithm.
Site stores only the encrypted password, also known as the “hash.”
User signs-in to the website, supplies password.
Site receives the password, encrypts the password using a one-way encryption algorithm.
Site compares the password in the database to the password supplied by the user.
The site does not need to know the exact password in order to know if the correct password was supplied. It only needs to compare password hashes. As safe as that sounds, it still presents a possible security exploit, and that's what we'll be discussing in this article.

The Exploit

One of the most popular cryptographic hash function is md5. It converts a string into a 32-digit hexadecimal number. Most web applications make use of this function. In PHP, you can use the following to generate the md5 hash of a string:

$sha256 = hash('sha256','password');
echo $sha256; // outputs 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8

Open in new window

Given the same input, the hashing function hash(), always returns the same output.  For example, the output for "password" (as seen above) will always be 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8. Web applications can compare password hashes without ever storing the cleartext password in the database.  That is good.  However, clever (devious) people are now maintaining databases of the hashed values of various commonly-used hashing functions.

These hash values are stored in lookup tables called “rainbow tables” along with the original cleartext password that was used to generate them   And the data is available for easy lookup (you can try the above hashed value in a Google search, and you will learn the cleartext password that was used to generate the hash).

What does this mean? Storing passwords as password hashes can be useless if the password can be obtained from rainbow tables.
Ed. Note:
More specifically:  If your database gets compromised and somebody is able to obtain a list of password hash values from it, they can often determine some (or even many) cleartext passwords by comparing the hash values to a list of hash values for commonly-used passwords.

So, if your database security is breached (possibly by an insider), and if you have used a common/standard hashing function, then a "rainbow table" lookup could make many of your accounts vulnerable.
How do we solve this problem?

The basic idea is simple:  Make sure that the hash you store in your database will not be found in anybody's rainbow table.  One way to do that is to use a salt -- add some characters that are not actually part of the password to the input text when generating the hashed value to be stored in the database.  Then do the same when verifying the input password.

Almost any salt value will work to avoid the exploit.   I like to use a random sequence of characters.  The following are the functions I use when dealing with user passwords:

function makePass($password) {
 $salt = generateChars(8);
 return $salt.hash('sha256',$salt.$password);
function generateChars($length) {
 $chars = 'qwertyuioplkjhgfdsazxcvbnm098764321QWERTYUIOPASDFGHJKLZXCVBNM';
 for ($x=0;$x<$length;$x++) $ret.=$chars[rand(0,strlen($chars)-1)];
 return $ret;
function checkPass($input,$in_file) {
 $salt = substr($in_file,0,8);
 if ($salt.hash('sha256',$salt.$input)==$in_file) return true;
 else return false;

Open in new window

What this does is it generates an 8-character salt that is pre-pended to the password before and after the encryption. This would ensure the uniqueness of the password hash. The chances of salted password hashes appearing in a rainbow table is very low. But if you’re paranoid, you can increase the length of the salt. Below is a list of the steps in using salted password hashing utilizing the above functions.
User registers to the website, supplies password.

Site receives the password, encrypts the password using a one-way encryption algorithm.
      $hash = makePass($_POST[]'password']);

Site stores only the encrypted password, also known as the “hash.”

User signs-in to the website, supplies password.

Site receives the password.

Site checks the database for the password. Site encrypts the password using the randomized salt. Site compares password hashes.
      $is_logged = checkPass($_POST[]'password'],$database_result[]'password']);
LVL 13

Expert Comment

by:Dagan Hoover
Thumbs up. Some people are still not even hashing them!

Expert Comment

Can you show me an example where I would use the functions??  I would love to use this.

Author Comment

Hi palmtreeinfotech,

I'm glad you like to use this. The following is an example using the above functions.

Registration page:
$username = $_POST['username'];
$password = $_POST['password'];
$email = $_POST['email'];

if ($username == '') $err[] = 'Username is required';
else if (strlen($username) > 20) $err[] = 'Username too long';
// also check here if the username exists but for this example, let's skip that

if ($password == '') $err[] = 'Password is required';
else if (strlen($password) < 6) $err[] = 'Password must be at least 6 characters';

if (!filter_var($email,FILTER_VALIDATE_EMAIL)) $err[] = 'Email is invalid';

if (!is_array($err)) {
	$hash = makePass($_POST['password']);
	// Sql::exec("INSERT INTO users (userid,username,password,email) VALUES (NULL,?,?,?)",$username,$hash,$email);
	echo 'User registered!';
} else {
	echo 'Errors!<br />' . implode('<br />',(array)$err);

Open in new window

Login page:
$username = $_POST['username'];
$password = $_POST['password'];
if ($username != '' && $password != '') {
	// $res = Sql::exec('SELECT * FROM users WHERE username=?',$username);
	// $row = @mysql_fetch_array($res);
	$is_logged = checkPass($password,$row['password']);
	if ($is_logged) {
		// proceed with authentication, setcookie, store session to db (or use sessions)
	} else {
		// login failed. invalid password.

Open in new window


Featured Post

Starting with Angular 5

Learn the essential features and functions of the popular JavaScript framework for building mobile, desktop and web applications.

Join & Write a Comment

The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month