Using PBKDF2 for password hashing

In this article, I will be discussing the issues with traditional and well used password hashing algorithms, specifically SHA-512 and refactoring code that uses SHA (Secure Hashing Algorithm) to upgrade and use PBKDF2, a modern and much more resilient hashing algorithm.

IntermittentBug was initially developed using SHA-512 for hashing user passwords. While the algorithm is cryptographically secure, major developments with GPU hardware mean that passwords that are hashed with an algorithm such as SHA-512 can now be cracked in a matter of seconds.

GPU Cloud PaaS Services

This is defiantly a problem. I myself have implemented for many different applications a SHA-512 system for hashing passwords. SHA is very widely implemented and any compromised system where an attacker has a dump of the database with hashed passwords with or without the Salt is at a high risk of having the passwords cracked. In the example below, I have used a Salt which makes it more difficult to crack but even a SHA-512 setup with a Salt will not be safe from a determined attacker with access to high grade GPU hardware.

With the 4 major cloud providers, Amazon Web Services, Microsoft Azure, Google Cloud and IBM Cloud all offering GPU hardware for rent by the hour using a PaaS (Platform as a Service) model, having access to serious hardware has never been easier.
For about £200 per hour of PaaS GPU computation, you could crack around 500,000,000,000 candidate passwords hashed using SHA-512 every second, that’s five hundred billion passwords a second.

Thankfully to reduce this risk, you can implement the hashing algorithm PBKDF2 an acronym for (Password-Based Key Derivation Function 2) to significantly increase the time it takes to crack passwords, even using GPU based computer hardware.

PBKDF2 is designed to be slow and difficult for GPUs to run against. This works by hashing the same password thousands of times. The result is that its takes orders of magnitude longer to crack passwords, reducing the likelihood of conducting a successful brute force attack on your user’s password hash.

You will be glad to know that it’s easy to implement and even refactor your existing security methods that use less secure algorithms to instead use PBKDF2.

I will be showing you how to use PBKDF2 using C# with example unit tests and then I will be running through the refactoring process for my security code that depends on SHA-512 and upgrading to PBKDF2.

SimpleCrypto.Net

SimpleCrypto.NET uses a Nuget Package library that abstracts out the complex cryptographic code into a simple API call. I will be using this library to demonstrate hashing passwords using PBKDF2. Let’s start off with installing the package from Nuget and creating some simple unit tests.

To begin, open the Package Manager Console in Visual Studio and enter the command

Install-Package SimpleCrypto

This installs the DLL SimpleCrypto.dll

Let’s create a unit test that will create a hash of the password “Pa55word

[TestMethod]
public void TestMethod_PBKDF2HashPassword()
{
    ICryptoService PBKDF2 = new PBKDF2();
    PBKDF2.HashIterations = 100000;

    // start with the plain text password
    string PlainTextPassword = "Pa55word";

    /*
        this is the unique Salt for the user - if you are using 
        individual salts per user you will need to save it to yoru Database
        or dependant XML config 
    */
    string salt = PBKDF2.GenerateSalt();

    // This is the hash of the password stored in "PlainTextPassword"
    string PasswordHash = PBKDF2.Compute(PlainTextPassword, salt);

    // password validation - compare the generated plain text
    Assert.IsTrue((PBKDF2.HashedText == PasswordHash) == true);
}

When running this unit test, you will notice that line 18 which computes the hash takes over 1 second. This is due to the algorithm having a default of 100,000 iterations. This can be modified if you want more or less iterations than the default. You can change this value with the following code.

ICryptoService PBKDF2 = new PBKDF2();
PBKDF2.HashIterations = 100000;

Refactor existing SHA-512 code

Let’s have a look at my security class I’m using to hash passwords using SHA-512.

public class Security
{
	#region private fields

	private string _salt;
	private string _password;
	private SHA512 _shaMan;
	private byte[] _result;
	private int DataSize;

	#endregion

	#region Constructors
	public Security() { }

	public Security(string password)
	{
		// A fixed salt used for hashing. 
		_salt = "EEh89//-*/$*6^f";
		_password = password;
		DataSize = _password.Length + _salt.Length;

		_shaMan = new SHA512Managed();
	}

	#endregion

	#region public methods

	/// <summary>
	/// Returns the generated hash
	/// </summary>
	/// <returns></returns>
	public string ComputeHash()
	{
		byte[] data = new byte[DataSize];
		data = Encoding.UTF8.GetBytes(_password + _salt);

		_result = _shaMan.ComputeHash(data);
		return Convert.ToBase64String(_result);
	}
}

This uses the built in .NET library System.Security.Cryptography developed by Microsoft. I’m also storing a master salt value for all passwords. Its more secure to have a salt value per user but for this example, ill refactor the code as it is.

This is my security class refactored to use PBKDF2. The abstraction provided by the ICryptoService interface makes the code simpler and easy to understand.

using SimpleCrypto;

namespace IntermittentBug.DataObjects.Data
{
    public class Security
    {
        #region private fields

        private string _salt;
        private string _password;
        private ICryptoService _PBKDF2;

        #endregion

        #region Constructors
        public Security() { }

        public Security(string password)
        {
            _PBKDF2 = new PBKDF2();
            _salt = "100000.9oiBwMFtgGHyuIu2UY76Ad39ZWL/1crawCltvwaM0ZElNjA==";
            _password = password;            
        }

        #endregion

        #region public methods

        /// <summary>
        /// Returns the generated hash
        /// </summary>
        /// <returns></returns>
        public string ComputeHash()
        {
            return _PBKDF2.Compute(_password, _salt);
        }
    }
}

To make it more secure you could have a unique salt for each user. This is normally stored in your database along with your user data, but keep in mind that if your database has been compromised the attacker will have your salt and hash.

Also keep in mind that if you have existing user accounts that have a hashed password using a different algorithm, you will have to reset their passwords for them to log in. In this instance, it would be best to email all users telling them of the upgraded security to your application and that a password reset is necessary.

So, there we have it, a simple tutorial on how to upgrade your password hashing algorithm to protect against GPU brute force computation attacks. PBKDF2 is a recognized standard so well worth upgrading your system to improve password security.


JGilmartin Profile Image

JGilmartin

Technical Architect at Pinewood Technologies

Rating: 2890

C# Expert

Offline


Tutorial Statistics
  • Views: 2694
  • Comments: 0
  • Author: JGilmartin (2890)
  • Date: 1/5/2017 19:30
Tags
C# Visual Sudio

© 2016 - 2018 - IntermittentBug