A cryptographic hash function is a hash function which takes an input (or ‘message’) and returns a fixed-size alphanumeric string, which is called the hash value (sometimes called a message digest, a digital fingerprint, a digest or a checksum).
In cryptography, a salt is random data that is used as an additional input to a one-way function that “hashes” a password or passphrase. The primary function of salts is to defend against dictionary attacks or against its hashed equivalent, a pre-computed rainbow table attack.
Even small changes in the input text provide a drastically different, yet fixed length, string of characters.
So, in simpler terms:
- A hash function is an one-way function (you cannot turn the transformed input back to the way it was) which transforms a string of characters into a fixed-length series of characters and numbers.
- A salt is a series of random characters which are appended to the string before applying a hash function to it. The reason to this is to prevent dictionary attacks (attacks in which a lot of common passwords are tested)
Now that that’s out of the way, let’s go around the steps we should take to improve the security of the previous application. For this tutorial, I’ll be using the following scheme:
Credit: Taylor Hornby, adriancs
One difference compared to the previous article, where we set up the DB and the login interface, is that we’ll talk about storing passwords first, as we can’t exactly validate them without having them stored somewhere in the first place.
1. Generate a long random salt using a CSPRNG.
A cryptographically secure pseudorandom number generator(CSPRNG) is an algorithm that produces a pseudorandom sequence of bytes. What makes it cryptographically secure is that it is very hard for someone to distinguish its output from true randomness. Each password is going to have its own random salt attached to it.
In order to generate the random salt, we’ll be using RNGCryptoServiceProvider() whose sole job is to generate random numbers. We’ll be storing these numbers in a byte array. What GetBytes does is place the bytes in the array given.
2. Concatenate the password to the salt and hash it with a hashing function.
The hashing algorithm we’ll be using is called PBKDF2. The reason for picking this one is that it provides the advantage of being slow by design. That sounds weird, doesn’t it? It is more advantageous for a password encryption algorithm to be slow, because the slower it is, the more time it takes an attacker to try and brute-force. How slow the algorithm is determines how many passwords can an attacker attempt per minute.
Obviously, it is important that the algorithm is not so slow that it starts bothering the user, which is why we can configure how quick or slow it is.
The Rfc2989DeriveBytes class is going to be responsible for the hashing. 10000 represents the number of iterations the algorithm is going to perform (It will keep hashing the previous hash this number of times. This is what makes it slower by design).
Now it’s time to do the storing part. Using, yet again, a byte array, we’ll add the salt in front of the hashed password (this is called prepending). The size of the array is going to be 36 bytes, because both the hash and salt are fixed length: the hash is 20 bytes, and the salt 16.
Adding the salt in front of the hash is simply preference, it can be added anywhere in the string, technically speaking.
After adding the hashed password and its salt to the Byte array, we should convert it to a string and begin the next step!
3. Save both the hash and salt to the user’s database record.
This step depends on the way you handle your database. What you should do is add the newly-formed string in the DB entry of the user.
I presented the way I handled my DB in my previous article, so i am not going to repeat myself. The only difference is that instead of sending the plaintext password to the DB, I instead send the savedPasswordHash string.
1. Retrieve the user’s salt and hash from the DB.
Similarly to saving, retrieving is dependent on how you’ve handled your database. I described how I handled it in more detail here. What is changed is that the query does not look for the username and password, just the username (which is either unique in the DB or nonexistent at all).
2. Concatenate the entered password to the salt and hash it.
If the username exists in the database (there is a person who has registered with this username) it’s time we take the new password input, salt it with the salt from the original password, hash it, then compare it to the input the user entered when registering. If they match, we allow logging in.
Firstly, we get the saved password hash from the DB, then we turn it into bytes for easier manipulation:
What we need to do now is hash the text input from the password textbox with the salt. We need to use the same function as before (so don’t change the number of iterations! You’ll get different results if you do and the passwords will show up as not matching even if they do). After hashing, place our new salted password hash in a byte array so we can begin the comparison of the stored and newly-formed hash.
3. Compare the hashed password in the DB with the entered one. If they match, grant access.
We’re going to use a for loop to compare each byte of the saved hash and the entered hash. We’re starting from the 16th byte in the stored password because bytes 0–15 are occupied by the salt.
If there are no differences found between the stored hash and the new one, grant access! 🙂