Page 1 of 1

Basic Login System Part I: Password Handling

#1 JackOfAllTrades  Icon User is offline

  • Saucy!
  • member icon

Reputation: 5951
  • View blog
  • Posts: 23,214
  • Joined: 23-August 08

Posted 24 October 2010 - 09:13 AM

*
POPULAR

A not insignificant number of people come to Dream.In.Code looking for how to create a Login System, so I thought I'd work on creating a basic, moderately secure system as a tutorial. Let's give it a try. We'll start with core of any login system, which is handling passwords.

The first tenet of password security is that one should NEVER save passwords in clear text! The proper way to save a password is by creating a cryptographic hash of a combination of a salt and the password. The use of a salt prevents an attacker who gains access to your password database from the use of rainbow tables to crack the passwords contained therein. Here is a static class which will hash, salt and compare passwords:

using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace BasicLoginSystem
{
    public static class PasswordHasher
    {
        /// <summary>
        /// Creates a byte array containing the hashed combination of
        /// a GUID-generated salt value prepended to the password, hashed
        /// according to the provided algorithm.
        /// </summary>
        /// <param name="password">The plaintext password to be hashed</param>
        /// <param name="hashAlg">The HashAlgorithm to use in hashing the data</param>
        /// <returns>A byte array of the concatenated salt & 
        /// hashed salt/password combination</returns>
        public static byte [] HashPassword(string password, HashAlgorithm hashAlg)
        {
            // Convert the password to a byte array
            byte [] passwordAsByteArray = UTF8Encoding.UTF8.GetBytes(password);

            // Generate a salt for the password
            byte [] saltAsByteArray = Guid.NewGuid().ToByteArray();
            
            // Hash the salt/password combination
            byte[] hashedPassAndSalt = HashPassword(
                saltAsByteArray, passwordAsByteArray, hashAlg);

            // Prepend the salt to the hashed data
            byte[] finalOutput = new byte[saltAsByteArray.Length + hashedPassAndSalt.Length];
            Array.Copy(saltAsByteArray, finalOutput, saltAsByteArray.Length);
            Array.Copy(hashedPassAndSalt, 0, finalOutput,
                saltAsByteArray.Length, hashedPassAndSalt.Length);

            return finalOutput;
        }

        /// <summary>
        /// Prepends the provided salt to the provided password using the provided
        /// HashAlgorithm
        /// </summary>
        /// <param name="salt">The salt to prepend</param>
        /// <param name="password">The password</param>
        /// <param name="hashAlg">The HashAlgorithm to use</param>
        /// <returns>A byte array containing the hashed salt/password combination.</returns>
        private static byte[] HashPassword(byte[] salt, byte[] password, HashAlgorithm hashAlg)
        {
            // Combine the salt and password into a single byte array
            byte[] passAndSaltForHashing = new byte[salt.Length + password.Length];
            Array.Copy(salt, passAndSaltForHashing, salt.Length);
            Array.Copy(password, 0, passAndSaltForHashing,
                salt.Length, password.Length);

            // Hash the salt/password combination
            return hashAlg.ComputeHash(passAndSaltForHashing);
        }

        /// <summary>
        /// Compares a plaintext password provided by the user to the
        /// password stored in a hashed salt/password byte array.
        /// </summary>
        /// <param name="password">The user-provided plaintext password for comparison</param>
        /// <param name="storedPassAndSalt">The stored hashed salt/password combination</param>
        /// <param name="hashAlg">The hash algorithm to use</param>
        /// <returns>True if the password provided matches the stored pass, otherwise
        /// false.</returns>
        public static bool ComparePassword(string password, 
            byte[] storedPassAndSalt, HashAlgorithm hashAlg)
        {
            // Get salt from start of storedPassAndSalt
            int hashSize = hashAlg.HashSize / 8;

            // Deduce the size of the salt from the hash length
            int saltSize = storedPassAndSalt.Length - hashSize;

            // Extract salt from storedPassAndSalt
            byte[] salt = new byte[storedPassAndSalt.Length - hashSize];
            Array.Copy(storedPassAndSalt, salt, saltSize);

            // Extract hash from storedPassAndSalt
            byte[] hashedPasswordFromFile = new byte[storedPassAndSalt.Length - salt.Length];
            Array.Copy(storedPassAndSalt, salt.Length, hashedPasswordFromFile, 0, hashSize);

            // Using the salt extracted from the storeed password,
            // hash the password we received from the user.
            byte[] hashedPasswordFromUser = HashPassword(
                salt, UTF8Encoding.UTF8.GetBytes(password), hashAlg);

            // Compare the stored and provided hashes
            return hashedPasswordFromFile.SequenceEqual(hashedPasswordFromUser);
        }

        /// <summary>
        /// Create a human-readable hexadecimal string from the
        /// byte array by walking the array and converting each byte
        /// into a 2-digit hexadecimal value.
        /// </summary>
        /// <param name="data">The byte array to make human-readable</param>
        /// <returns>The human-readable string</returns>
        public static string CreateTextString(byte[] data)
        {
            // Create a human-readable hexadecimal string from the
            // byte array by walking the array and converting each byte
            // into a 2-digit hexadecimal value.
            StringBuilder sb = new StringBuilder(data.Length * 2);
            for (int i = 0; i < data.Length; ++i)
            {
                sb.AppendFormat("{0:x2}", data[i]);
            }
            return sb.ToString();
        }

        /// <summary>
        /// Transform the provided human-readable hexadecimal string to
        /// an array of bytes.
        /// </summary>
        /// <param name="data">The string to transform</param>
        /// <returns>The byte array representation of the hexadecimal string</returns>
        public static byte[] CreateByteArray(string data)
        {
            // Since each byte is represented by a 2-digit hex number,
            // we know that the length of the resulting byte array is
            // half the length of the passed-in data.
            byte[] binData = new byte[data.Length / 2];
            for (int i = 0; i < data.Length; i += 2)
            {
                binData[i / 2] = Convert.ToByte(data.Substring(i, 2), 16);
            }

            return binData;
        }
    }
}



The publicly-accessible HashPassword function will take a given password and generate a salt based on a generated GUID, then hash the two and prepend the salt to the result, taking advantage here of the fact that any given hash function returns a hash which is of a known size.

When we use the ComparePassword function we can extract the salt from the beginning of the data -- based on the known size of the hashed data -- and reverse the process performed in the aforementioned HashPassword function. That is, we

  • Extract the salt and the hashed salt + password combination
  • Hash the salt and the provided password using the provided algorithm
  • Compare this calculation with what was saved in the file/database


If the data compares favorably, then we know we have a successful login.

The next part of the tutorial will create the login system interface, through which we can save the login data, starting with the simplest: a text file.

Is This A Good Question/Topic? 9
  • +

Replies To: Basic Login System Part I: Password Handling

#2 skorned  Icon User is offline

  • New D.I.C Head

Reputation: 13
  • View blog
  • Posts: 41
  • Joined: 30-August 08

Posted 25 October 2010 - 01:22 PM

Thanks, I can see this being useful, I had spent quite a while trying to find similar tutorials for PHP earlier.

But does this mean you're prepending your salt in plaintext in front of the hashed salt+password? I couldn't trace through your code carefully, but this is the suggestion I'm getting from your later explanation...Could you maybe give a sample run of it? Like give a sample password, salt, use a common hashing algorithm like md5, and then say what value would be stored in database? I couldn't quite follow this in your code...


Also, how exactly do GUIDs work? Does your code get assigned one GUID, that doesn't change every time you compile?
Was This Post Helpful? 0
  • +
  • -

#3 JackOfAllTrades  Icon User is offline

  • Saucy!
  • member icon

Reputation: 5951
  • View blog
  • Posts: 23,214
  • Joined: 23-August 08

Posted 25 October 2010 - 03:00 PM

The GUID is only generated at account creation time or if the user changes the password. And yes, it's prepended as is to the hashed value of it and the password. So it's stored as
salt+H(salt+password)


Salts are often stored alongside the hashed data. See here for a decent discussion of salt storage. You could conceivably use the same salt for every password you hash, but if an attacker gets that they can use it to build a rainbow table out of it for directly attacking your database, although that's not really all that practical.

A GUID is a 128-bit globally-unique identifier I used for convenience. Here's an example of the output of the hashing algorithm for SHA256 with the password "ThisIsMyPassword" and a generated GUID (which I did not take note of), which has been converted from a byte array to a human-readable string:

fe12645fd4254941afa8d54d7e9a49f6f1330ee466cd455545f7a6da40f01498c654121748755956469b24e3a130449d


I would avoid the use of MD5 as a hashing algorithm as it's been broken (see here). SHA-1 has also been broken.

The same principles apply to any password storage system, be it C# or PHP or any other language/framework.
Was This Post Helpful? 1
  • +
  • -

#4 Tenderfoot  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 11
  • View blog
  • Posts: 160
  • Joined: 21-March 12

Posted 31 March 2012 - 03:46 AM

Great tutorial, thanks for this. I have a couple of questions though but am not sure whether to ask them here or in C# general, could anyone tell me which would be better?

I've made a console application using this, just to see how it works, but I intend to add it to my final assignment in my programming course for fun.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1