12

I've taken a look at the StackOverflow question, "Password Encryption / Database Layer AES or App Layer AES," and I'd like to effectively and efficiently hash my passwords on registration (web app) and then be able to check they are correct on login. I'm using VB, but comfortable using C#.

I would love to use Jeff Atwood's Encryption class described in ".NET Encryption Simplified" as it's really easy to understand. It has a hashing class—but I have no idea how to "login" and compare hashes after they have been hashed. This is Jeff's demonstration of his hash methods using his Encryption class:

Sub DemoHash()
    Dim d As New Encryption.Data( _
        "{ts '2004-10-09 08:10:04'}The world is beautiful and needs caring by its children")

    Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA1)
    Dim hash2 As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
    Dim hash3 As New Encryption.Hash(Encryption.Hash.Provider.SHA384)
    Dim hash4 As New Encryption.Hash(Encryption.Hash.Provider.SHA512)
    Dim hash5 As New Encryption.Hash(Encryption.Hash.Provider.MD5)
    Dim hash6 As New Encryption.Hash(Encryption.Hash.Provider.CRC32)

    hash.Calculate(d)
    hash2.Calculate(d)
    hash3.Calculate(d)
    hash4.Calculate(d)
    hash5.Calculate(d)

    Console.WriteLine("SHA1:   " & hash.Value.Hex)
    Console.WriteLine("SHA256: " & hash2.Value.Hex)
    Console.WriteLine("SHA384: " & hash3.Value.Hex)
    Console.WriteLine("SHA512: " & hash4.Value.Hex)
    Console.WriteLine("MD5:    " & hash5.Value.Hex)
    Console.WriteLine("CRC32:  " & hash6.Calculate(d).Hex)
    Console.WriteLine()

    Dim salt As New Encryption.Data("salty!")
    Console.WriteLine("Salted CRC32:  " & hash6.Calculate(d, salt).Hex)

    Console.WriteLine("Press ENTER to continue...")
    Console.ReadLine()
End Sub

So my questions are:

  1. I can encrypt the password (though I have no intention of storing it) and hash a string. If I were to have a user called 'barry' with a password of 'fishlegs', what is the best way to store his password and retrieve it?

  2. In SQL Server; is binary or nvarchar the best option for the storage of the hash?

  3. Based on 'barry' and his password what effectively is the hash storing? Is it an encryption of 'fishlegs' appended to a salt?

Cryptography is hard!

Thanks to anyone who can assist...

Community
  • 1
  • 1
dooburt
  • 3,010
  • 10
  • 41
  • 59
  • I was goign to answer this but everone else has. You have the wrong end of the stick. – Omar Kooheji May 19 '09 at 15:38
  • Is there a reason you are using "Hxxp://" in your links? I would assume it was a typo and fix it, but sense you've done it twice, it looks intentional. I'm just curious what the reason is. – Grant May 19 '09 at 15:40
  • Hi Grant, I got the message that users couldn't create weblinks, so I removed the double-t and replaced with an x :) – dooburt May 20 '09 at 08:02

8 Answers8

85

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.

Aly Elhaddad
  • 1,913
  • 1
  • 15
  • 31
Chad Birch
  • 73,098
  • 23
  • 151
  • 149
  • 7
    Brilliant! This is not an answer, is a complete class.. ;-) – Fabricio Araujo May 19 '09 at 20:33
  • Thank you very much Chad! Your answer was clear and concise and contained all the information I needed. My understanding had some grey areas and this explained everything perfectly. Thank you very much. – dooburt May 20 '09 at 08:00
  • Chad, I can't upvote you enough. All SO answers should be so well written. – theog May 22 '09 at 23:29
  • 1
    I know this is an old post, but I have a quick point to raise. You mention salting with a randomly generated salt. But how do you expect the code to then reuse that salt when comparing the passwords? Do you want the salt to be saved? In the database? If so, then that's pretty useless because if the attacker gets access to the DB, they will be able to see the salts that were used for each password as they would have to be stored along side the row in the DB that the salt applies to... –  Jul 29 '12 at 07:12
  • @navnav Read the entire answer again: "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." – BlazingFrog Jul 30 '12 at 22:07
  • @BlazingFrog That's not the point. The **radnomly generated** salt will have to be stored somewhere. I asked where it should be stored. If in the DB, then the attacker has access to it. I know the salt will still (partially) prevent the rainbow attacks, but being able to access the salt still makes things easier for the attacker. –  Jul 30 '12 at 22:11
  • 1
    @navnav I believe it's you who are missing the point. Your answers are all in Chad's post: "... so we store that in the database, along with the salt string." Again, if you read the entire post you'll see that he simply explains how to make it harder for the attacker, not 100% secure: "But when someone is trying to generate rainbow tables to crack a password, having each entry take several seconds is an absolute killer." Cryptography 101: it's all about making it harder to crack, not 100% secure. I would certainly not call that "pretty useless". – BlazingFrog Jul 31 '12 at 03:26
7

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.

blowdart
  • 55,577
  • 12
  • 114
  • 149
  • blowdart, Superb - thanks also. Coupled with the other answers and Chad's excellent theory on encryption this is fantastic ;) – dooburt May 20 '09 at 08:04
  • I've been looking for far too long this evening for an example that sets out the native .NET solution in plain english - thanks, really helpful (+1) – Chris Dec 01 '09 at 20:31
  • I tried using the code but getting error `'hashBytes' does not exist in the current context ` and `The name 'plainTextWithSaltBytes' does not exist in the current context` for the method `ComputeHash`. Can anyone provide the full implementation code. Thank You!!! – user4221591 Nov 20 '17 at 18:23
  • @user4221591 I've replied to your question here https://forums.asp.net/p/2132105/6178144.aspx?p=True&t=636468165462832952 – Jayendran Nov 21 '17 at 07:16
6
  • 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.

Daniel Brückner
  • 59,031
  • 16
  • 99
  • 143
  • Seems a very good solution to stop external hackers who don't have access to values in the database and who can't see the code. However (like most cases I suppose) you'd only need an internal spy to crack this one fairly easily. – DBMarcos99 Jul 13 '09 at 18:23
  • Correction - would still need a lot of brute-force computing power, to recover the password. But knowing the stored Salt value and that it's prefixed to the password, would at least make things feasible (if unlikely). Hands up - still one of the best methods I've seen on this issue. – DBMarcos99 Jul 13 '09 at 18:34
  • Salting just prevents using a precalculated rainbow tables. If you know the salt, you can build a rainbow table for one specific salt, but it will be of little use because you cannot reuse it for other passwords because they all have different salts. Hence using salted hashes turns the cracking effort from a rainbow table lookup into building a rainbowtable per password. – Daniel Brückner Jul 14 '09 at 14:18
  • If one is affraid that prefixing the password with a salt is weak (maybe one day a weakness is descovered that allows to split the hash calculation), you can modify this schema. I usually interleave password and salt yielding SSe4cLr7e5ta!1T, but I think this is of little use - maybe they will discover a weakness that allows to decompose the hash calculation for interleaved patterns. In this case interleaving weakend the schema compared to prefixing. Further I usually don't interleave the password and the salt, but calculate hash(salt) and hash(password), interleave both hashes, and – Daniel Brückner Jul 14 '09 at 14:24
4

Don't confuse encrypting passwords with hashing them; with a good cryptographic hash function, you should not be able to reverse the hashing process to retrieve the original string.

Chad's answer above is an excellent point-by-point explanation of the concepts involved.

This subject's been done to death all over the Internet; not just on Stack Overflow; seriously - a simple web search ought to find you a pretty comprehensive guide.

Thanks, Jeff, for spreading yet another bunch of misinformation. I suppose we'll see a whole slew of misguided questions about hashing, encryption, etc. over the next week, not to mention the crap that'll come up about floating-point arithmetic.

Rob
  • 47,999
  • 5
  • 74
  • 91
  • It maybe worthwhile pointing out that this question wasn't borne out of Jeff's latest post on codinghorror.com, it was just a coincidence! – dooburt May 20 '09 at 11:16
3

I believe the process is like this: Generate a random salt, so 'fishlegs' + 'r4nd0mS4lt' to get 'fishlegsr4nd0mS4lt'. Then you hash that, say with MD5 (though you might want to use SHA256 to be more secure) to get: 593d5518d759b4860188158ef4c71f28. Store that and the randomly generated salt. When the user logs in, append the random salt and then check if his entered password with the salt matches the hash in the database.

CookieOfFortune
  • 13,836
  • 8
  • 42
  • 58
3

What you are essentially wanting to do is:

A) On account creation, you get a username and password from the user, you hash those together with your own salt and store the resultant string in your database like:

Dim sUserName = "barry"
Dim sPassWord = "fishlegs"
Dim mySalt = "A deliciously salty string! fillED WIth all KindS of Junkk(&^&*(£"
Dim d As New Encryption.Data(mySalt + sUserName + sPassWord)
Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
hash.Calculate(d)
Dim sTheSaltedHashedUnPassCombination = hash.Value.Hex;
SavenewPasswordHashToDatabase(sTheSaltedHashedUnPassCombination)

You never store sPassWord.

B) When the user logs in you perform exactly the same operation on the provided username and password then compare the resultant hash to the previously stored value in the database so you use:

Dim sThePreviouslyCreatedHashFromTheDatabase = GetHashForUserFromDatabase(usernameProvided)
Dim mySalt = "A deliciously salty string! fillED WIth all KindS of Junkk(&^&*(£"
Dim d As New Encryption.Data(mySalt + usernameProvided+ passwordProvided)
Dim hash As New Encryption.Hash(Encryption.Hash.Provider.SHA256)
hash.Calculate(d)
Dim sTheSaltedHashedUnPassCombination = hash.Value.Hex;
if (sThePreviouslyCreatedHashFromTheDatabase.Equals(sTheSaltedHashedUnPassCombination))
    'UN & Password Valid!
else
    'UN & PW Invalid!
end

(Pardon any errors, VB aint my language)

To answer your given questions:

1) See above. Never store the password directly, store the hashed value

2) use a char(X), the number of characters returned from the hash is constant so you can give it a known storage size.

3) What you are effectively storing is the password for the user salted with their username and also salted with the constant on your server. This will be a fixed length string and you cannot change this string back into the separate username and password.

Wolfwyrd
  • 15,716
  • 5
  • 47
  • 67
  • Thanks Wolfwryd, following Chad's theory, this is perfect code examples to back it up. Shame I can't mark more than one answer as correct... – dooburt May 20 '09 at 08:04
2
  1. The whole point of hashing is that you store the hashes rather than the plaintext passwords and they can't be recovered (at least only with great difficulty). Hash functions are designed to be one-way, so that retrieval is impossible - this is their very essence.
  2. nchar[n] is probably the best option, since common hash algorithms produce results of constant length (SHA1 outputs a 160-bit hash). Common practice is to convert the hash to Base64 so that it can be stored as ASCII text.
  3. A salt is just a method that adds random bits onto a hash in order to complicate the process of cracking. Each extra bit of salt means that a brute-force cracker must take twice as long (and use twice as much memory).
Noldorin
  • 144,213
  • 56
  • 264
  • 302
0

ASP.NET includes a SQL based membership provider that allows you to create, store and verify encryped or hashed passwords, without you having to do anything - as Eric Lippert says:

let me give you all my standard caution about rolling your own cryptographic algorithms and security systems: don't.

Community
  • 1
  • 1
Zhaph - Ben Duguid
  • 26,785
  • 5
  • 80
  • 117
  • That being said, I find the ASP.NET Membership system exceedingly ugly on the UI. Although it can be styled it takes a monumental effort to do so. If you simply securing some basic user details for a run-of-the-mill website and not storing anything overly sensitive then following many of these patterns and practises to "roll your own" stripped down membership system should be an option. At least in my opinion. – dooburt Jun 11 '09 at 11:28
  • I've never found it difficult to style, but then I guess that's because I've always used it with the CSS Control Extenders: http://www.asp.net/cssadapters/ These will be "Out of the box" in ASP.NET 4.0 as well. – Zhaph - Ben Duguid Jun 11 '09 at 18:28
  • I guess it would be worthwhile me revisiting them and taking a good look at the cssadapters you've linked to. I have now incorporated a fairly simple and basic user system with hashed and encrypted passwords and am thoroughly pleased with the results. It's secure, quick and I've learnt a stack. I'll now look a the ASP.NET ones to see what they offer... – dooburt Jun 12 '09 at 11:23