Do not use string hashes for security purposes

Do not use string hashes for security purposes

  • Comments 11

A recent question I got about the .NET CLR's hashing algorithm for strings is apropos of our discussion from January on using salted hashes for security purposes. The question was basically "my database of password hashes doesn't seem to work with .NET v2.0, what's up with that?"

To make a long story short, the answer is under no circumstances should you use String.GetHashCode for security purposes. It was not designed for anything other than hash table balancing. If it hurts when you do that then stop doing it!

The slightly longer version goes like this. Suppose you want to store some secrets in a database, but you only need to be able to confirm that the user knows the secret. As I discussed in my series on salted hashes, a hash is a commonly used tool for this task because a cryptographic hash has some nice properties. Namely, it is a fixed number of bits (in the 100's of bits range), small changes to input produce huge changes in output, and it is very difficult to go from the hash back to the original secret. Another nice property that I didn't call out in my earlier article is that there are industry-standard hash algorithms where you can be reasonably guaranteed that any two implementations will produce the same results when given the same set of bits.

The .NET CLR string hash algorithm has none of these nice properties, and therefore is completely unsuitable for a cryptographic hash function. Specifically:

1) The string hash algorithm was designed to be blindingly fast rather than hard to run backwards, so it is likely that a mathematically astute attacker will be able to rapidly deduce facts about the input knowing only the hash.

2) Worse, being only 32 bits, using brute force to find a message that produces a given hash becomes doable in an afternoon with a PC rather than a trillion years.

3) The string hash algorithm is not an industry standard.

4) GetHashCode is not guaranteed to produce the same behaviour between versions. And in fact it does not. The .NET 2.0 CLR uses a different algorithm for string hashing than the .NET 1.1 CLR, and 64 bit versions of the CLR use different algorithms than the 32 bit versions.

If you are saving .NET 1.1 CLR hash values in a database then you will not be able to match them when you upgrade to 2.0. The hash algorithm was specifically not designed to be forward/backward compatible and we called that out in the documentation. Do not rely on compatibility when we specifically call out in the documentation that you should not rely on version-to-version identical output for a function.

Please don't do that; you're just asking for a world of hurt if you do. Use SHA2 or some other algorithm designed for that purpose. Yes, I know that weaknesses have been discovered in some standard hashing algorithms, but they are still orders of magnitude better than a hash designed for hash table balancing!

  • Wow. It never even dawned on me that someone might do that. And this was someone who had read your series? Talk about understanding just enough to get it completely wrong.
  • Soo... what is the supposedly-painless alternative that we should be using instead?

    Roll our own algorithm and hope it behaves according to standards?

    Buy a third-part component?

    Hope we don't need standard behavior and don't expect upgrades?
  • >Soo... what is the supposedly-painless alternative that we should be using instead?
    >Roll our own algorithm and hope it behaves according to standards?
    >Buy a third-part component?
    >Hope we don't need standard behavior and don't expect upgrades?

    Plenty of standard hashing algos built into .NET... look in the System.Security.Cryptography namespace in 1.1 and you'll find the following (plus a few more):

    System.Security.Cryptography.MACTripleDES
    System.Security.Cryptography.HMACSHA1
    System.Security.Cryptography.MD5CryptoServiceProvider
    System.Security.Cryptography.SHA1Managed
    System.Security.Cryptography.SHA256Managed
    System.Security.Cryptography.SHA384Managed
    System.Security.Cryptography.SHA512Managed
  • I think there is some confusion here and you should probably point out WHICH methods specifically you are talking about.

    I think you are talking about calling GetHashCode() on a string, is that right? If so then I understand this post... But it appears as in the question by Eric K. that you may be referring to some OTHER hashing methods when you say "the .NET string hashing algorithm" (a very vague statement).

    Please clarify...?
  • G. Dog: Follow the last link in the article.
  • * no, this was clearly not someone who had read my articles -- this was from a customer who had developed their own customer-secret hashing system. The question just got to me at random from a PSS guy.

    * The alternative you should be using instead is exactly what I said -- use SHA1 or MD5 or some other industry-standard algorithm designed for security purposes. Make your own educated judgment as to what algorithm suits your purposes based on the characteristics of your problem and the algorithm in question.

    * Yes, I am talking about String.GetHashCode. I apologize for the unclear text; I've clarified it.

  • Is there some way, then (possibly using Reflection) to get the old behavior?

    I can easily imagine a situation where somebody has persisted a hash table (caching web pages with the filename being the URL's hash value in base64) and would want to keep the same behavior after upgrading to Whidbey.
  • Reflection isn't going to help you -- reflection just turns early-bound calls into late-bound calls, it doesn't change what the call does.

    If you are in this unfortunate predicament, the best advice I can give you is to get the Rotor sources, look up the string hash function, and re-implement it in the managed language of your choice. Then start using your now-stable library function rather than String.GetHashCode.

  • One of my favorite function names:

    System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile()
  • If I understand correctly, the weaknesses in MD5 and SHA-1 are with respect to collisions, not preimages, and as long as those hashing algorithms remain preimage resistant, then there's really no problems with continuing to use them.
  • Gabe: there is a way to get the old functionality:

    http://aspalliance.com/1218_NET_String_Hashing_The_Hidden_Knot.all

Page 1 of 1 (11 items)