Secure Password Hashing
One of the first things any web developer will come across when coding for a business client is a back-end, or admin section that will allow employees to add, edit, and delete certain information used in their site. For instance, lots of companies have a page of FAQ’s that their visitors might want to peruse. One of the simplest ways to allow them to keep these FAQ’s up-to-date is to create a portion of their site that only they will have access to and can easily change this type of information. How do you limit access to pretty much anything on the web? Create an account where they have to login with a username and password!
The highest priority when creating these admins is security. You don’t want just anyone to be able to login and change whatever they want. You have to make sure that only authorized users can gain access. But completely securing anything on the internet is, dare I say, impossible. All you can really do is make it as hard as possible for these would-be intruders. The first step is the password.
Every back-end should have a corresponding database table of authorized users. This table stores information such as a unique id for each user, their name, their login, and their password. Whatever you do, do not store their password as plain-text. All an intruder needs to do is gain access to your database, and he/she has all the passwords clear as day. When saving passwords, you need to hash them, or store a fixed-length encrypted version of them.
PHP has two of the simplest functions to do this: MD5() and SHA1(). Both are algorithms that process a string and convert it into a one-way hash, meaning they can’t decrypt that hash to get the password. They can only encrypt another string and compare the two hashes to see if the passwords match. So to save a person’s password initially, use:
$insert = mysql_query(”INSERT INTO admin_users (user, pass) VALUES (’$username’, ‘md5($password)’) “);
To check for the correct password each time they login, use:
$select = mysql_query(”SELECT * FROM admin_users WHERE user=’$username’ AND pass=’md5($password)’ “);
Easy to do, right? Unfortunately, not very secure. MD5 only uses 128-bit encryption which returns a 32-character hexadecimal (combination of 0-9 and a-f) string, and SHA1 only uses 160-bit encryption which returns a 40-character hexadecimal string. By themselves, they might be strong enough for a back-end where the only thing users can edit is an FAQ. But let’s make it a lot harder to break by adding something very simple: a salt.
A salt is a string that is prepended or appended to the user’s password before it’s hashed. One of the ways that an intruder will try to break in is by using a dictionary attack. A dictionary attack uses a large list of strings that can be used as passwords along with their respective hash. Checking the stored hash against the dictionary, it will only be a matter of time before he finds the password. Adding a salt makes the password more complicated, and will take that intruder much longer to find. Exponentially longer. Using a different salt for each user makes more sense, because a new dictionary would need to be created for each user rather than using the same dictionary for the whole list of users. So where do we get the salt?
The easiest place is from that user’s stored information. You should already have a unique user id and a unique login. Why not use them both? The longer the salt, the better. Just make sure whatever you use for the salt cannot change over time, or else the hashes would not match and that user couldn’t log in. For this reason, be careful when using any information that is user-editable such as their name. If you used their first name as part of the salt, and user James wanted to change his name to Jimmy, he’d have a bit of a problem on his hands. Storing our user’s information now becomes:
$insert = mysql_query(”INSERT INTO admin_users (id, f_name, l_name, user, pass) VALUES (’$id’, ‘$f_name’, ‘$l_name’, ‘$username’, ‘md5($id . $password . $username)’) “);
And authorizing the login becomes:
$select = mysql_query(”SELECT * FROM admin_users WHERE id=’$id’ AND f_name=’$f_name’ AND l_name=’$l_name’ AND user=’$username’ AND pass=’md5($id . $password . $username)’ “);
This method should be strong enough to thwart most would-be intruders if your back-end doesn’t permit access to any senstive information, such as company secrets, social security numbers, or other account information such as credit card and personal identification numbers. If you’re going to give anybody access to information that is that sensitive and private (which should only be accessible via an encrypted connection like SSL), then you need to up the ante a little bit.
PHP has another hashing function, hash(), that gives you more secure hashing algorithms such as sha256 (256-bit encryption that returns a 64-character hexadecimal string) and sha512 (512-bit encryption that returns a 128-character hexadecimal string), among many others. Use of this function, however, is only available since PHP version 5.1.2 so please make sure you are using a version later than that. Using one of these more secure algorithms along with a longer salt mentioned above, you’ve got a good start to making sure only authorized users are getting access to you or your clients’ sensitive information.
Tags: md5, password hashing, sha1
