Effective Password Encryption Effective Password Encryption asp.net asp.net

Effective Password Encryption


Hmm, I think you're just missing some basic concepts related to how hashing works. Let me try to explain briefly. I'm going to start out simple and elaborate on my answer afterwards, so please read through the whole thing, the information at the beginning will not be secure.

What you want to use to store a password is a function known as a "one-way hash". What this means is that, for any input that you feed the function, the same input will always give the same result. However, there is no mathematical process that lets you take that result string and figure out what the original input was.

Let's take MD5 as an example of a hashing function. If I run MD5 on the string "password", I will always get the result "5f4dcc3b5aa765d61d8327deb882cf99". However, if you were to simply give someone that result string ("5f4d..."), it is impossible for them to apply some mathematical process to "reverse" the function and figure out that it came from "password".

What this means is that when a user first sets up their password, you apply a hashing function to it, and store the result. So instead of storing "password", you store "5f4dcc3b5aa765d61d8327deb882cf99". Then, when that user tries to log in, you take whatever they typed into the password box on the login form, and apply the same hashing function. If you get the same result as what's stored in the database, they must have entered the same password as they originally chose, even though you have no idea what that original password actually was.

Now, even though it's impossible to "reverse" a hash function, the fact that the same input always gives the same output means that someone can simply build up a big database of input/output pairs, and use that to effectively reverse hashes. This is called a "rainbow table". There are many of these available on the internet, so it's not safe to use simple hashing, just in case your database ever gets compromised. That is, even though it is mathematically impossible to take "5f4dcc3b5aa765d61d8327deb882cf99" and figure out that it came from running MD5 on "password", it's very easy to determine that in practice. All you have to do is run every word in a dictionary through MD5 and store the results, and you can easily reverse simple passwords.

This is where "salting" comes in. If you generate a random "salt" string for every user and attach that to their password, it effectively ruins rainbow tables. For example, let's say that the same user above registers with their password as "password". We generate a random 8-character salt to attach to their password before hashing it. Let's say that it's "A4BR82QX". Now, instead of hashing "password", we hash "A4BR82QXpassword". This gives the result "87a4ba071c8bcb5efe457e6c4e6c4490", so we store that in the database, along with the salt string. Then when this user tries to log in, instead of directly hashing and comparing the password they entered in the login form, we take what they entered, put "A4BR82QX" in front of it again, and hash that. Just as before, if it matches the stored hash, we know that they entered the right password.

Effectively what you've done here is make it so that pre-generated rainbow tables are useless for trying to crack the passwords in your database. Since the salt is random, and each user has a different one (generally), the attacker will have to re-generate their rainbow tables for every individual user. This is much more difficult.

However, there's one more problem, and that's that generating MD5 hashes is fast. Even though salting like this requires them to re-generate rainbow tables, because of how fast MD5 is, some decently-complete rainbow tables can be created very quickly. So if they just want to crack a high-value account on your site, it's not really a big deal for them to spend some time generating rainbow tables to try and reverse that password. If the high-value account's original password wasn't secure enough by itself, it'll still be found very quickly, even with salting.

So the next step is to find a slow hash function, and use this instead of a fast one like MD5. Having your site take an extra couple of seconds to check a login isn't a big deal at all. But when someone is trying to generate rainbow tables to crack a password, having each entry take several seconds is an absolute killer. I've written enough here, so I'll just finish by linking to this article, which goes into plenty of detail about picking a good, slow hashing function: Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes.

That was a pretty huge answer, if any of that's unclear, please let me know in a comment and I'll edit to elaborate.


OK so a couple of things about Jeff's class first of all. SHA1 and MD5 are deprecated now. CRC32 is not suitable at all for passwords. Secondly you should be salting every hash, preferably with a different salt value. Generally you choose a cryptographically random block of data for this, but at a push you could use the user name. To salt you prefix, or suffix the salt value somewhere in the process. I tend to hash the password, hash the salt, combine the two, then hash again. But you can swap things around it doesn't really matter that much, as long as you are consistent.

So rather than confuse things further with Jeff's class lets do this the classic way.

First off random salt generation.

public static byte[] GetRandomSalt(){  int minSaltSize = 16;  int maxSaltSize = 32;  Random random = new Random();  int saltSize = random.Next(minSaltSize, maxSaltSize);  saltBytes = new byte[saltSize];  RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();  rng.GetNonZeroBytes(saltBytes);   return saltBytes;}

Then hashing

public static byte[] ComputeHash(string plainText){  byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);  HashAlgorithm hash = new SHA256Managed();  return hash.ComputeHash(plainTextWithSaltBytes);}

So this will compute a SHA256 hash and return it as a byte array.

If you were salting you'd do something like the following

byte[] passwordHash = ComputeHash(password);byte[] salt = GetRandomSalt();byte[] saltHash = ComputeHash(salt);byte[] hashWithSaltBytes = new byte[hashBytes.Length + saltBytes.Length];for (int i=0; i < hashBytes.Length; i++)  hashWithSaltBytes[i] = hashBytes[i];for (int i=0; i < saltBytes.Length; i++)  hashWithSaltBytes[hashBytes.Length + i] = saltBytes[i];

And then, if you're bored hash it down again, or leave as is.

To turn a byte array into a string, if you don't fancy storing bytes you can use

string hashValue = Convert.ToBase64String(hashWithSaltBytes);

String comparisons are easier than byte comparisons where you have to iterate over each array, up to you. Just remember if you are using random salts you need to store them beside the password.


  • Take the user password "Secret!".
  • Generate a random salt of a few bytes "s4L75a1T".
  • Concat them to "s4L75a1TSecret!".
  • Calculate the hash "b9797a5b683804eb195b6ba2a5e368ae74394cd3" (this is SHA-1 in hex)
  • Store the hash and the salt (both as hex string, Base64 string, or whatever you like) in the database together with the user name and the other user information (in the example the salt is just a plain string and the hash is Base64 encoded).
    FirstName    LastName    Salt        Hash    -----------------------------------------------------------------    John         Doe         s4L75a1T    uXl6W2g4BOsZW2uipeNornQ5TNM=

If you want to verify the user password, just take the user name, look up the salt, do the above again and see if the calculated hash and the one in the database match. There is no (known) way to (easily) recover the password.