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.Cryptography
namespace 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.ToBase64String
to 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.GetBytes(newSalt));
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.
SHA256 is absolutely not a safe algorithm for hashing passwords. It is not designed to prevent brute force attacks, it is simply too fast. A salt is not sufficient protection.
A real password hashing function/KDF such as Argon2 must be used to protect against modern brute force attacks.
While hashing and salting are good things, for the love of all things that are good, please do not hash passwords using an algorithm that is not intended for hashing passwords.
Use something like PBKDF2, or better yet, more modern algorithms like BCrypt, SCrypt, or Argon2. Better yet, don’t store passwords. Use an OAuth2 identity provider that does all the hard work for you.
When you generate a salt, do not use Math.Random – it is not designed to provide cryptographically random values, which makes your salt values less valuable.
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#upgrading-legacy-hashes
If you are using a new salt value for a new password entry do you store the salt value as well in the db holding the password?
Thanks
Hi Doug,
Yeah, you would need a way to retrieve the salt used for the original password when it was hashed, so that when you come to authenticate the password, you can use the same salt to hash and check the value against the one in the DB. Doesn’t necessarily need to be in the same DB or table but you would need to be able to link it back to the original password.
So, Finally, we generated Hashed Password from this Code and Storing it in the database. Now My Question is How to validate the Hashed password to authenticate the User with his Present password.
In order to validate an incoming password, you would need to take in the plain text entered by the end-user and then hash this in the same way you hashed it when it was saved. You also need to retrieve the salt that was used when the password was originally hashed and use that when performing your hash. If the hashed result of the incoming password is the same as the hashed password you have in the database for that user, they have successfully authenticated.
Great post!
One correction: this line is missing a left parenthesis on getting the `newSalt` byte[]
var hashedPassword = ComputeHash(Encoding.UTF8.GetBytes(password), Encoding.UTF8.GetBytesnewSalt));
Ah thank you so much!
Glad you caught that for me. Thanks for letting me know. 🙂