Password security is often quite challenging to understand for those who are new to it (I’ve been there too, as you can see from my question about salting on StackOverflow). In this article, I am hoping to make this fascinating topic a little easier to understand. We’ll be covering two important techniques called hashing and salting. Although passwords are typically stored in a database, we’ll be using a C# dictionary to keep it simple.

Clear Text Passwords

To get started, create a new Console Application. Add the following near the top, so that we can use dictionaries:

using System.Collections.Generic;

Just inside your class Program, before your Main() method, add the following dictionary to store our users and their corresponding passwords (see “Morse Code Converter Using Dictionaries” if this seems in any way new to you):

Notice that when requesting the password, we’re setting the console’s text colour to black. The console’s background colour is also black, so the password won’t show as you type, fending off people trying to spy it while looking over your shoulder.

Press F5 to try it out:

Awesome – we have just written a very simple login system.

The problem with this system is that the passwords are stored as clear text. If we imagine for a moment that our usernames and passwords were stored in a database, then the actual passwords can easily be obtained by a hacker gaining illegal access to the database, or any administrator with access to the database. We can see this by writing a simple method that shows the users’ data, simulating what a hacker would see if he managed to breach the database:

We can then add the following code just before the final Console.ReadLine() in Main() to test it out:

Console.WriteLine();
Hack();

This gives us all the details, as we are expecting:

This isn’t a nice thing to have – anyone who can somehow gain access to the database can see the passwords. How can we make this better?

Hashing

One way is to hash the passwords. A hash function is something that takes a piece of text and transforms it into another piece of text:

A hash function is one-way in the sense that you can use it to transform “Hello” to “8b1a9953c4611296a827abf8c47804d7”, but not the other way around. So if someone gets his hands on the hash of a password, it doesn’t mean that he has the password.

Another property of hash functions is that their output changes considerably even with a very small change in the input. Take a look at the following, for instance:

You can see how “8b1a9953c4611296a827abf8c47804d7” is very different from “5d41402abc4b2a76b9719d911017c592”. The hashes bear no relationship with each other, even though the passwords are almost identical. This means that a hacker won’t be able to notice patterns in the hashes that might allow him to guess one password based on another.

One popular hashing algorithm (though not the most secure) is MD5, which was used to produce the examples above. You can find online tools (such as this one) that allow you to compute an MD5 hash for any string you want.

In order to use MD5 in our code, we’ll need to add the following statement near the top of our program code:

using System.Security.Cryptography;

At the beginning of the Program class, we can now create an instance of the MD5 class to use whenever we need:

private static MD5 hashFunction = MD5.Create();

If you look at the intellisense for MD5, you’ll see that it has a ComputeHash() method, which returns an byte array, rather than a string:

We’re going to do some string work, so add the following near the top:

using System.Text;

Let’s write a little helper method to hash our passwords, using strings for both input and output:

public static string Hash(String input)
{
// code goes here
}

In this method, the first thing we need to do is convert the input string to a byte array, so that ComputeHash() can work with it. This is done using the System.Text.Encoding class, which provides several useful members for converting between strings and bytes. In our case we can work with the ASCII encoding as follows:

The “x2” bit converts each byte into two hexadecimal characters. If you think about it for a moment, hexadecimal digits are from 0 to f (representing 0-15 in decimal), which fit into four bits. But each byte is eight bits, so each byte is made up of two hex digits.

Anyway, after that, all we need to do is return the string, so here’s the entire code for the method:

In this way, we aren’t storing the passwords themselves, but their hashes. For example, we’re storing “5f4dcc3b5aa765d61d8327deb882cf99” instead of “password”. That means we don’t store the password itself any more (if you ever signed up to an internet forum or something, and it told you that your password can be reset but not recovered, you now know why). However, we can hash any input password and compare the hashes.

In our Login() method, we now change the line that checks username and password as follows:

if (users.ContainsKey(username) && users[username] == Hash(password))

Let’s try this out (F5):

When the user types “johnny” as the username and “password” as the password, the password is hashed, giving us “5f4dcc3b5aa765d61d8327deb882cf99”. Since the passwords were also stored as hashes in our database, it matches. In reality our login is doing the same thing as it was doing before – just that we added a hash step (a) when storing our passwords and (b) when receiving a password as input. Ultimately the password in our database and that entered by the user both end up being hashes, and will match if the actual password was the same.

How does this help us? As you can see from the hack output (last four lines in the screenshot above), someone who manages to breach the database cannot see the passwords; he can only get to the hashes. He can’t login using a hash, since that will in turn be hashed, producing a completely different value that won’t match the hash in the database.

Although hashing won’t make the system 100% secure, it’s sure to give any potential hacker a hard time.

Salting

You may have noticed that in the example I used, I had some pretty dumb passwords, such as “password” and “password123”. Using a dictionary word such as “flowers” is also not a very good idea. Someone may be able to gain access to one of the accounts by attempting several common passwords such as “password”. These attempts can be automated by simple programs, allowing hackers to attempt entire dictionaries of words as passwords in a relatively short period of time.

Likewise, if you know the hash for common passwords (e.g. “5f4dcc3b5aa765d61d8327deb882cf99” is the hash for “password”), it becomes easy to recognise such passwords when you see the expected hash. Hackers can generate dictionaries of hashes for common passwords, known as rainbow tables, and find hashes for common words used as passwords.

We can combat such attacks by a process known as salting. When we compute our hashes, we add some string that we invent. This means changing the first line of our Hash() function as follows:

byte[] inputBytes = Encoding.ASCII.GetBytes("chuck" + input);

Both the database password and the one entered by the user will be a hash of “chuck” concatenated with the password itself. When the user tries to login, it will still work, but look at what happens now:

The login worked, but the hashes have changed because of the salt! This means that even for a password as common as “password”, a hacker cannot identify it from the hash, making rainbow tables much less effective.

Summary

This article described how to store passwords securely. It started off by doing the easiest and worst thing you can do: store them as clear text. A hash function was subsequently introduced, to transform the passwords into text from which the password cannot be retrieved. When a user logs in, the hash of the password he enters is compared with the password hash stored in the database.

Finally, the hashes were salted, by adding an arbitrary piece of text to them, in order to transform the hashes into different values that can’t be used to identify common passwords.

Additional Notes

It is interesting to note that with hashes, it does not matter how long your password is. The hash is typically fixed-length (depending on the hash function you use). So if you create an account on some airline’s website and it tells you that your password is too long because they have some maximum limit… well, they don’t know what they are doing.

Hashing and salting make password storage a lot more secure. The next level is using a slow hash algorithm with a work function. You can read about this in my followup article, “Secure Authentication with BCrypt“.

6 thoughts on “Securing Passwords by Salting and Hashing”

I know the MD5 is there to “illustrate a point” and you appropriately call out that it is “not recommended”, it would be nice if this were spelled out more clearly as “This is in no way shape or form secure, and easy to brute force. Please see the bcrypt article for proper password hashing”

I’ve seen too many developers copy code that was “illustrating” something like this and only skimmed it, and missed the warning.

I think it’s good that it’s here so people can learn, but I think this poses some risks of accidental use.

Valid point, but wherever I put the warning (in the beginning or at the point where MD5 is discussed), people might miss it. The fact that many developers do things quickly rather than learning to do them right is a widespread problem, and it will happen no matter how many warning signs I put in my articles.