Hashing and Salting Passwords in C#

September 17, 2020 By Nick Proud

UPDATE: This example previously used a SHA256 algorithm to hash and Math.Random to generate a salt, it has been correctly pointed out that there are stronger algorithms for hashing that are recommended. I’ve updated this post to use PBKDF2 instead. This may not be everybody’s choice of algorithm, so use whichever you feel is strongest. It’s clear that SHA256 should never have been used for passwords so thanks to the community for their feedback, allowing me to correct a mistake.

Are we at the point now where the golden rule of ‘not storing passwords in plain-text’ should be a given? I would hope so, but that doesn’t change the fact that there are many ways to disguise sensitive data being saved to databases, so I thought I’d give a basic example of how it can be done in C#.

The Basics – ‘Hashing’ you say?

Hashing is where we use a hash function to convert (or map) values that have variable size to a value that is fixed, therefore making the resulting hash value usually shorter than the hashed value.

In simpler terms, a hash function allows us to create a new value which is a representation of the value we passed to the function after it has been manipulated by the function’s hashing algorithm, So why is this useful to us when we store passwords?

Passwords are often stored in databases, and what commonly happens to databases in the 21st century? They are breached. (Or at the very least, the risk of a breach is highly prevalent.) If you are storing passwords in plain-text and your database is breached, then a whole bunch of passwords are now available for malicious use. We can all agree, this is bad right?

So this is where hashing comes to the rescue. Instead of storing plain-text passwords inputted by our users, we can hash the password before it is saved to the database. This means that if the data did fall into the wrong hands, it’s of no use. What’s more, we as the keepers of the data don’t even know what the user’s password is. We just get the user to enter their plain text password, which then uses a hashing function to hash the value and store what looks like garbage.

Is this enough? I would say when it comes to passwords, the precautions we take are never enough. Like it or not, it is still not desirable for these hashed passwords to fall into malicious hands. Once a password has been found, if the attacker wanted to, they could still try as many hashing algorithms on as many values as they wanted to try and find the plain-text password they need to get into your account. The difference is, we made it harder for them.

We can make it even harder for the bad guys by sprinkling a little salt onto our passwords before we hash them.

What is a ‘Salt?’

A ‘salt’ is a random value that we add to the value we are hashing before it is hashed. The purpose of this being that it adds uniqueness to the data compared to other values we hashed using the same algorithm and therefore dramatically increases the effort required to brute force a hashed password.

For example, we have already established that we in our table of passwords, we are hashing each one before it is saved. If we add a random value, (a unique value for each password we are storing) to the password before we hash it, we make the hashes more unique. It also defends against the possibility that multiple users may share the same password. This is a risk. Suppose one password was compromised – the attacker managed to find the plaintext value for a hash they obtained from the database. They now have not only the password for the hash they found but also the password for anybody using the same value. They know to look for identical hashes to the one they broke, increasing the number of compromised passwords. A salt would combat this because even though users may have entered the same plain text password, the resulting hashes are unique.

Hashing and Salting – An Example in C#

Thanks to the higher level of abstraction afforded to us by .NET languages, hashing and salting in C# is a relatively simple affair. Before we get hashing, let’s look at how we can generate a salt.

Essentially, we are going to generate a random string that we can use add uniqueness to the value we wish to hash. To achieve this, I have created a method called ‘GenerateHash’ which you can see below

using System;
using System.Security.Cryptography;
using System.Text;


public string ComputeHash(byte[] bytesToHash, byte[] salt)
{
    var byteResult = new Rfc2898DeriveBytes(bytesToHash, salt, 10000);
    return Convert.ToBase64String(byteResult.GetBytes(24));
}

 

Let’s unpack this method and see how it has been built.

First, we create an instance of RDC2898DeriveBytes (part of the System.Security.Cryptographynamespace which you will need to reference as I have above) and call the Create()passing in the bytes we want to hash, the salt (which will be generated in another function demonstrated later on) and the number of iterations we are going to have on the algorithm – the higher the better, so I’m using 10000.

We then pass GetBytes(24) to get the bytes (you can use whichever size hash size you need, I’m using 24) to a converter, converting the bytes to a base64 encoded string for serialization.

Great! We now have a hashed password which has been strengthened further with a random string in the form of a salt. But, how did we get the salt in the first place?

Here’s an example of how we can write a function to generate a new salt, which we would call for every new password we store, in order to ensure each password has its own unique salt.

public string GenerateSalt()
{
    var bytes = new byte[128 / 8];
    var rng = new RNGCryptoServiceProvider();
    rng.GetBytes(bytes);
    return Convert.ToBase64String(bytes);
}

Again, let’s go through the code.

First, we create a byte array for our value to go into. Then we create an instance of RNGCryptoServiceProvider which allows us to use the method GetBytes() to get a random byte array into our created byte array.

Finally, we use the Convert.ToBase64Stringto get our string salt which we can then be used by RDC2898DeriveBytes in the hashing function.

So if I wanted to generate a salt and then use it to hash a password, I could write

var password = "mypassword123!";
var newSalt = GenerateSalt();
var hashedPassword = ComputeHash(Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytesnewSalt));

That’s it! This is just one of the many ways we can defend our stored passwords. However, it is realistically the bare minimum.

Hashing with a salt should be the very least you implement to secure passwords you are storing in a database.

Share