Gal Ratner
Gal Ratner is a Techie who lives and works in Los Angeles CA and Austin TX. Follow galratner on Twitter Google
Secure user authentication with one way password hash


Keeping users passwords in your database is a part of almost every application, yet securing passwords is rarely being done correctly.

I recently read an article by Coda Hale about the ineffectiveness of password salts. Coda Suggested using bcrypt to store passwords. He reasoned his argument by explaining bcrypt is extremely slow to compute, therefore making it slow to hack.

I completely agree, however, I wanted to add another way of safely storing passwords in a more conventional way by hiding the salt in the hash. The idea wasn’t mine. It belongs to a DBA named Scott Hulberg. It’s pretty simple and for the sake of this blog post I am not going to implement it completely. I am going to prepend the salt to the password hash, making it invisible to a hacker. You can go further by writing an algorithm to plant the salt in the hash array as you see fit.

Since the only way to match a one way hashed password is to use the salt we used to generate this hash, if a hacker cannot get to the salt, they cannot retrieve the original password.
Let’s begin by composing the method to create our hash and prefix it with the salt:

 

public static readonly int PASSWORD_SALT_LENGTH = 16;
 
    /// <summary>
    /// Generate a one way hash from a password.
    /// </summary>
    /// <param name="password"></param>
    /// <returns></returns>
    public static byte[] GetHashedPasswordBytes(string password)
    {
        // Generate random bytes as salt.
        // Prepend the salt and compute the hash along with the password
        // Then prepend the salt to the hashed result
 
        byte[] salt = PasswordUtils.GetRandomBytes(PasswordUtils.PASSWORD_SALT_LENGTH); // Get a random array of bytes
        byte[] enteredPassword = Encoding.UTF8.GetBytes(password); // Get the password bytes
        List<byte> passwordByteList = new List<byte>(); // Prepend the salt to the password
        passwordByteList.AddRange(salt);
        passwordByteList.AddRange(enteredPassword);
        byte[] passwordHashResult;
 
        using (SHA256 shaM = new SHA256Managed())
        {
            passwordHashResult = shaM.ComputeHash(passwordByteList.ToArray()); // Get the hash
        }
        passwordByteList.Clear();
        passwordByteList.AddRange(salt); // Prepend the salt Hash result
        passwordByteList.AddRange(passwordHashResult);
        return passwordByteList.ToArray();
    }
 
    /// <summary>
    /// Get an array of random bytes
    /// </summary>
    /// <param name="size">The size of the array</param>
    /// <returns></returns>
    public static byte[] GetRandomBytes(int size)
    {
        byte[] random = new Byte[size];
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
        rng.GetNonZeroBytes(random);
        return random;
    }

 

This is the method to re-generate a password hash at login time:

 /// <summary>
    /// Compares a generated password hash to the original password.
    /// </summary>
    /// <param name="password">The original password</param>
    /// <param name="passwordHash">The hash</param>
    /// <returns>Thre if the password matches</returns>
    public static bool ComparePasswordHash(string password, byte[] passwordHash)
    {
        bool isValidated = false;
        byte[] salt = new byte[PasswordUtils.PASSWORD_SALT_LENGTH];
        byte[] hash = new byte[passwordHash.Length - PasswordUtils.PASSWORD_SALT_LENGTH];
        // userPassword is composed from the hash value and the salt.
        // seperate the salt from the hash value and use the salt to compute and compare the passwords
        Array.Copy(passwordHash, salt, salt.Length);
        Array.Copy(passwordHash, PasswordUtils.PASSWORD_SALT_LENGTH, hash, 0, hash.Length);
        byte[] enteredPassword = Encoding.UTF8.GetBytes(password);
        List<byte> passwordByteList = new List<byte>();
        passwordByteList.AddRange(salt);
        passwordByteList.AddRange(enteredPassword);
        byte[] passwordHashResult;
        using (SHA256 shaM = new SHA256Managed())
        {
            passwordHashResult = shaM.ComputeHash(passwordByteList.ToArray());
        }
        if (PasswordUtils.CompareBytes(passwordHashResult, hash))
            isValidated = true;
        return isValidated;
    }
 
    /// <summary>
    /// Compare two byte arrarys
    /// </summary>
    /// <param name="array1"></param>
    /// <param name="array2"></param>
    /// <returns></returns>
    public static bool CompareBytes(byte[] array1, byte[] array2)
    {
        if (array1.Length != array2.Length)
            return false;
 
        for (int i = 0; i < array1.Length; i++)
            if (array1[i] != array2[i])
                return false;
 
        return true;
    }


Now to log in a user we are going to compare the original hash to the new hash. If the hash matches, then the user has entered the correct password:

var passwordHash = PasswordUtils.GetHashedPasswordBytes("test");
        if (PasswordUtils.ComparePasswordHash("test", passwordHash))
            Console.WriteLine("Password mached");


As you can tell, unless a hacker knows where they salt is stored, they cannot retrieve it and cannot re-compute the original password.
Thank you Brad Laney for sending me the original  Coda article.

Shout it


Posted 5 Jul 2011 9:34 PM by Gal Ratner
Filed under:

Powered by Community Server (Non-Commercial Edition), by Telligent Systems