Storing Passwords - The Wrong, Better and Even Better Way

21 Jun 2009
If you've ever had to sign up to use a website, you'll no doubt have been prompted to provide a username and password, so that when you next visit the site you can login without having to fill in all of your details again. Your password has to be stored somewhere, otherwise you won't be able to login the next time you visit. Right? Unfortunately, a few sites I've come across do just this. They store your password, which means if the information is stolen, someone has got your password.

In a perfect world, everyone would use a different password for every account they sign up for, and that password would be a combination of numbers, letters (uppercase and lowercase) and special characters, and would be at least about 20 characters long. But let's be honest here, we're not all memory machines and remembering cryptic combinations like that isn't something everyone can do. So people are tempted to choose just one password and use it on everything they sign up for, including their email account. Which means there's the potential for someone to have the email address and the password. Not a very good thing.

There's no way around this, the weakest point in any security system is the human element. People are always going to chose easy passwords, or use the same passwords for multiple sites. So it's up to us as web developers to help to keep the passwords secure so that these people never have to go through the problems associated with someone getting into their other accounts.

This all comes down to how you store the password. Do you do it the “Wrong Way”, the “Better Way” or the “Even Better Way”? (I'm not going to say the “Right Way”, because I don't think there is such a thing when it comes to password security).

The Wrong Way



I was shocked to recently discover a website which stored their passwords in plain text. I couldn't quite grasp how in modern web development anyone could store their passwords like that, but it still happens. Storing passwords as plain text is a very bad thing. It means that if I sign up for a website using the password of “iamawesome”, then the value that's stored in their database is also “iamawesome”. This means any employees with access to the database (and there will always be at least one), can see your password. They can also probably see your email address associated to the account. So if you're one of those people that uses the same password, there's nothing to stop that person going and getting into your accounts. I like to think the majority of people out there in this position are honest and would never use that information for such nefarious means, but by the same token, I'm willing to bet there are people out there who aren't so honest.

So how can you tell if a website stores your passwords securely? The simple answer is that you can't. But there are a few tell tale signs that they're not doing all they can. Many websites have a “Forgotten password” function. If you go through this procedure and you get an email with your original password in, then it is highly likely they're storing you password in plain text. (Although this isn't always the case, they could be using reversible encryption, but again.. the developers will now how to reverse the encryption, so while it may stop anyone who steals the database, it wouldn't stop dishonest employees).

Personally, I would stop using such a service straight away (and in most cases I will email the relevant department to warn them that they're storing things insecurely). There is no need for a website to store that information in plain text. None at all. It puts your information at risk. There's a much better way to store passwords.

The Better Way



In order to login to a website, there's no need for the site to actually know what that user's password is, they just need to be able to tell if you've entered the same password as when you registered.

You can use something called a “hash” to do this. A hash is a one-way mathematical function, which given an input A, will always produce the same output B. But ideally it's very difficult to get from B back to A (but not impossible, you can use a rainbow table to lookup A based on the hash B).

Note : It is possible for more than one value to result in the same hash. This is called hash collision. It's pretty unlikely to get hash collision unless you're using very large datasets, but it is a possibility. I've never come across it myself though. But it does happen.

Different algorithms will hash things in different ways, and some are better than others. Some, such as MD5, have almost complete rainbow tables, so it's possible to find all the values of A which could map to the hash B, etc. MD5 is now considered an insecure hashing algorithm and shouldn't really be used to secure passwords on it's own. Others, such as the SHA-2 family of algorithms, remain secure at the moment and have no complete rainbow table lookups. I use SHA-256 myself. (SHA-1 also has some weaknesses such as hash collision, but it's better than MD5).

PHP 5 has built in functions for hashing with SHA-256


$password_hash = hash("sha256", "iamawesome");
// 4aa4029d0d0265c566c934de4f5e0a36496c59c54b6df8a72d9c52bdf0c1a0e8


The idea behind using a hash, is that you store this hash in your database, rather than the plain text password. In order to verify a user has entered the correct password when logging in, you use the same hash function on whatever text they enter, then compare the result to what you have stored in the database. If they match, then the correct password was entered.


$user_entered = hash("sha256", $_POST['password']);
return ($user_entered == $password_from_db);


This way, only the user ever knows the real password. If someone were to look at the database of your stored hashes (whether it's a dishonest employee, or because it was stolen) they'll only ever be able to see the hash, and won't be able to go around getting into people's email accounts.

So you might be thinking that it's problem solved, let's just do this hashing stuff and we'll be secure. Well no. There are still some problems with this method. Suppose two people have the same password, this means they will have the same hash in the database. Now suppose you manage to trick one of these people into giving you their real password (via however, email scam, etc). This means you would now have the password for anyone with the same hash.

Also, one of the most common password attacks is called a dictionary attack. It involves trying every dictionary word in order to login as someone, as it's quite common for people to just use a dictionary word as their password. A dictionary attack is still possible when using hashes. You can generate a list of hashes from common dictionary words and compare it to the hashes in the database, you'll probably find a few matches, and then you have the password for those people.

So really all we've done is obfuscated the password from people viewing it directly, but certain attacks are still possible. There is still a better way.

The Even Better Way



The way around this problem is to use something called a “salted hash”. The definition of a salt is “random bits that are used as an input to a key derivation function”. Normally to create your hash you provide one thing as input (the original password) and you get the hash as an output. A salt is a random string of characters you use as another input into the hash function in order to get the output.

So now when storing a users password, you can take the hash as you had before, but also generate a random hash, concatenate this with the password hash, and then hash that result again. So in PHP it would look something like this,


$salted_hash = hash(hash($password) . $random_salt);


You can make your function however you want, as long as the random salt and password are both used in order to construct the hash you want to store. The method you use doesn't change the effectiveness of your password storage, one is not really any more secure than the other. Relying on the design of how you hash the password and salt together to provide security is called security through obscurity and should be avoided, since in reality the method you use doesn't affect the security.


$salted_hash = hash($password . $random_salt);
$salted_hash = hash(hash($password) . hash($random_salt));
// ...etc


In the database, it's also important to store the salt for each user as well as the completed salted hash, otherwise you won't be able to tell if the user has entered the correct password. Each user should have their own random salt, you shouldn't use the same one for the entire database or for multiple users, otherwise you've completely negated the point of using a salt in the first place.

You're table should look something like this,


username         password_hash          password_salt
rich             2bae773debd80de        e99aa878c15879a554f
bob              d82ff2c12d5065f        d59cdc5abc7f7f3a207


So now, this means the hash you store will be different for every user in your database, even if they have the same password. So if anyone wants to do a dictionary attack, they have to do it for each user individually, rather than the entire database at once, making it much more difficult. This method also makes using a rainbow table to attack the password quite difficult and often infeasible, since an attacker would need to precompute rainbow tables for each possible salt value.

(Note: You can never make it impossible to crack someone's password, there will always be a way. You can just make it very very difficult)

But now you have another problem, how do you implement the “I've forgotten my password” functionality if you can't tell the user their password?. Rather then just retrieving the user's original password (which you can't do when using salted hashes), you instead want to either generate a new one for them and email it to the address you have for their account, or to verify the user by other means (such as security questions) and ask them to reset their password once you are sure they are who they say they are. Most mainstream websites nowadays will do this (and if they don't then I'd be very worried).

If you have dishonest employees, they won't be able to get the passwords. If someone were to steal your database, they'd have to perform a dictionary attack on every single user in the database. Making it very difficult for them.

Anyway, that's my two cents. Anyone have any better ways to store passwords?

I was prompted to write about this after posting my tip on Tipster and then coming across a project which still stored passwords as plain text.

Mia Rencontre

Mia Rencontre

Thu 25th Jun at 22:16

When I worked in Customer Support, there were always customers who of course lost their password to their shopping account. They contacted us to retrieve their original password and got sometimes really mad that we couldn't provide it for them. Fact is there was no password stored anywhere. If they used the "forgot your password" function, they would get a code and the invitation to use it as a temp password but to select a new one. This would also make them mad because the system wouldn't allow for them to choose the same password as the forgotten one :))

Amoureuse

Amoureuse

Tue 7th Jul at 01:34

You could use special software for Password storage . For the past few months I've been using Access Manager, it's really useful and it's free.

Versicherung

Versicherung

Tue 27th Oct at 05:03

Hey Amoureuse, can you send me a link for Access Manager? I will test it.

Write a comment

Your comment will be moderated before it will appear on the site.

Tags