Built with 
HomeBrave Tech WorldAbout SiteMarcelo CalbucciMy Videos

Brave Tech World

Week 40
SMTWTFS
78910111213

Entries for October 9, 2006


October 9, 2006


MON
9
OCT
2006

How do I calculate a MD5 hash from a string?(The right way!)

By Marcelo

 

    I just came across this postOpen in a new window on the official C# FAQ blog on MSDN. It shows a very simple way of computing an MD5 out of a string. As the post says:

"...It is a common practice to store passwords in database using a hash ..."

    And they go showing an example of a function that has this signature:

public string CalculateMD5Hash(string input)

    Can you spot the security vulnerability here?

 

    Here is the problem, although a Hash is a one-way algorithm, the same string will always result on the same MD5, and that is good and bad. It is good because you can test passwords to see if the hash that they generate are the same as the one stored on the database, it is bad because somebody can use a dictionary or brute force attack to figure out 60-70% of the passwords on the database in just a couple of hours.

 

   "Emily" will always be encoded as "D8EA48BC5A82A9FD6B80F70DD51FC30C". So, somebody with a decent size dictionary of names, cities, and other words will be able to figure out a lot of the passwords.

 

    But these people that understand security are very smart (smarter than me for sure) and they came up with a thing called a "salt".

 

    A salt is a just a random sequence that helps to generate a different MD5 for each password. This means that if user #127 uses the password "Emily" and user #538 also use the password "Emily", each will have a different MD5 if they had different salts.

 

   I wrote about what makes a good authentication database schema on another post just two months ago.

 

   Now, the problem with the function from the MSDN blog is that it doesn't tell you how to use it right, which is the case with must security related documents on MSDN, and, by consequence why so many developers write so crappy code in regards to security.

 

How you do it right...


    First, you create a database schema (or save an XML file, or whatever) that contains at least the following information:

  • UserId, Username, Email and whatever other information you need
  • PasswordHash: Either a char(32) or a byte(16)
  • PasswordSalt: An integer (int32) will suffice.

    Second, add this function somewhere on your code:


static public MD5 md5 = MD5.Create();

static public string ComputePasswordHash(string password, int salt)

{

   byte[] inputBytes = Encoding.UTF8.GetBytes(password + salt.ToString());

   byte[] hash;

   lock(md5)

   {

      hash = md5.ComputeHash(inputBytes);

   }

   StringBuilder sb = new StringBuilder(32);

   for(int i = 0; i < hash.Length; i++)

   {

      sb.Append(hash[i].ToString("X2"));

   }

   return sb.ToString();

}

 

    Notice that this function creates a single static MD5 constructor and make sure that only one thread can use it at a time -- this is faster, trust me (or measure it yourself).

 

    Third, when a user creates an account (or update his password) you generate a random integer for his salt, which, BTW, could be a hash of his username combined with his IP address, and then you compute the hash, like this:

 

public void SetUserPassword(int userId, string password)

{

   int salt = DateTime.Now.Ticks % 0xFFFFFFFF;

   string hash = ComputePasswordHash(password, salt);

   // Save the 'salt' and 'hash' to the DB

}

 

    Fourth, every time a user tries to sign to your system, you check to see if the password is a match on the database:

 

public bool IsCorrectPassword(int userId, string password)

{

   int salt;

   string hash;

   // Load the 'salt' and 'hash' to the DB

   string hash2 = ComputePasswordHash(password, salt);

   return String.Compare(hash, hash2, true);

}

 

 

    This might look more complicated than the simple example from MSDN (and it is), but it is the only correct way of using MD5 Hashes to store passwords.

 

    Using the MSDN example somebody could do a brute force attack on the database and figure out most of the passwords, and would just take a couple of hours to do it. If you use salt, you make the computational cost hundreds of thousands of times harder, which means that instead of a couple of hours to crack your database we are talking about decades!

 

    And just to makes matters a tad more interesting, I usually use a double salt. This is, there is a fixed string in the code that I add to the computation of the hash so that if somebody gets my database it also needs to get the source code (or the binary) to make his/her life less miserable when cracking the hashes.

 

 

 



Similar Content
Powered by Google