Thursday 23 May 2013

How TeamMentor creates SHA256+PBKDF2 password hashes and stores them in XML files

In the 3.3. version of TeamMentor there was a significant change in how the user's password hash is submitted and stored.

In version 3.2. we used a SHA256 hash of “username+password” (created either on the client (browser) or server) which was stored in the user’s xml file (which is a serialization of the in memory user-object).

Although we never stored the user’s password on disk (in fact in most cases we never even sent it to the server), due to advances in processing power and Credentials Brute Force Attacks, in 2013 that is not a secure way to store password anymore.

After some threads (see TM stores passwords insecurely issue), it was agreed that a solution based on PBKDF2 should be used.

And since we had the requirement to migrate the previous user’s details in to the new 3.3 PBKDF2 format, we made the new algorithm compatible with the previous SHA256 mode.

Here is the formula that we currently use:
  1. user provides a password in clear text
  2. we create a SHA256 of the username+password
  3. we create a 64 bit PBKDF2 of the SHA256 using 20000 interactions using the user’s ID (a GUID) as the salt
In another words:

passwordHash = PBKDF2 ( SHA256 ( UserName + Password ) , UserId_GUID )

with:
  • UserName, UserId_Guid and passwordHash stored in the User’s XML file
  • Password never stored (but transmitted over SSL)
  • UserID_GUID (the salt) is a .NET GUID which is a 128-bit integer, i.e. 16 bytes (note: although a bit OTT, just to add one more layer of security, in the 3.4 release we will be using RNGCryptoServiceProvider to create it (see Use a stronger random number generator  issue and the Is RNGCryptoServiceProvider is 'fast enough' to create a GUID post)
  • the PBKDF2 byte size is 64  (which creates a password hash that looks like: 8jesPsP9ExGeoMe/N ezXqh7RWQTdawsUb0znfo6VgD46nRIbAXbcgaPYCRlfLYQK1IeQphESxjZ5EDc/ZD0yFw== (base64 format))
  • there are 20000 PBKDF2 interactions

And how was this implemented?

Well, let’s look at the code:

When the user logs in, its username and password are submitted via a WebService method which calls a Login method that calls tmUser.createPasswordHash(password)

image

...which will call  the createPasswordHash (this TMUser tmUser, string password) method

image 

... that first calls the hash_SHA256(this string text, string salt) method with the Username as text and Password as salt (this will calculate the same hash has the one created in 3.2)

image

... and then feeds that SHA256 hash to the hash_PBKDF2(this string password, Guid salt) method (with the User’s ID GUID used as salt)

image

The end result is a hash that looks like this when stored in the user’s XML file:
 <?xml version="1.0"?>

    <TMUser GroupID="1" State="The State" Country="The Country" EMail="TM_alerts@securityinnovation.com" Company="The Company" Title="El Title" LastName="LName" FirstName="FName" UserName="admin" UserID="735868140" ID="d8aac161-0e25-426c-b21c-9cd230be7dba">

        <SecretData SessionID="22449f90-b463-4626-9361-6b131276f745" CSRF_Token="1428559804" 

                    PasswordHash="8jesPsP9ExGeoMe/NezXqh7RWQTdawsUb0znfo6VgD46nRIbAXbcgaPYCRlfLYQK1IeQphESxjZ5EDc/ZD0yFw=="/>

         <AccountStatus UserEnabled="true" PasswordExpired="false" ExpirationDate="0001-01-01T00:00:00"/>

         <Stats LoginFail="2" LoginOk="17" LastLogin="2013-05-22T15:16:18.3987974+01:00" CreationDate="2013-05-16T15:16:22.8624556+01:00"/>

             <UserActivities When="130131890354274532" IPAddress="::1" Who="admin" Detail="admin" Action="User Login"/>

Does this protect against brute force attacks?

I believe so.

Because even in the worse case scenario where the entire userdata xml files are compromised (equivalent of an SQL injection that retrieves all user data), the attacker would need to brute force an SHA256 with salt + 20000 interactions of 64 byte PBKDF2 in order to find 1 (one) user's password.

And since each user has unique salts, the attacker would need to do this for each user.

Which is a lot of work/effort, and means that there are probably easier ways for the attacker to get the account details, just like xkcd very cleverly captures with his http://xkcd.com/538/  cartoon:


If you made it this far, you should also be interested in:
And these amazing posts from the password related blog posts from Troy Hunt: