views:

94

answers:

3

Once I've generated a salt and hashed the password (using bcrypt, etc) would it be best to store the results as a string or as a byte array in the database? Are there any benefits either way? Or is it a more subjective decision?

+2  A: 

VARCHAR should be sufficient. In my opinion VARCHAR data will be easier to consume and work with vs. binary.

The built in ASP.NET membership providers also store hashed passwords as VARCHAR fields.

Wallace Breza
+1  A: 

If you use VARCHAR either make sure the bytes are either valid characters or force the bytes to ASCII before you save the hash and salt. To make is safer from data corruption you may want to either encode the bytes in Base64 or Hexadecimal before you store them in the VARCHAR field.

For example, if you store the byte[] output from MD5 or SHA1 some of the byte values may be removed or replaced when the byte[] is converted to text is they are not valid UTF-8/Unicode. This should not be as much of a problem if you are using ASCII but it could still cause data corruption.

If you don't want to deal with Hex you could use this base64 method Convert.ToBase64String that is built into the framework.

If you use VARBINARY and don't try to use and text encoding methods this issue should not exist, but could make comparing hashes more difficult.

...Note that if you use NVARCHAR this data corruption could still occur...

static void Main(string[] args)
{
    var salt = "MySalt";
    var password = "MyPassword";

    var saltedKey = CalculateHash(salt, password);

    Console.WriteLine(saltedKey);
    // MySalt$teGOpFi57nENIRifSW3m1RQndiU=

    var checkHash = CheckHash(saltedKey, password);
    Console.WriteLine(checkHash);
    // True
}

private static string CalculateHash(string saltOrSaltedKey, string password)
{
    var salt =
        saltOrSaltedKey.Contains('$')
        ? saltOrSaltedKey.Substring(0, saltOrSaltedKey.IndexOf('$') + 1)
        : saltOrSaltedKey + '$';

    var newKey = Encoding.UTF8.GetBytes(salt + password);

    var sha1 = SHA1.Create();
    sha1.Initialize();
    var result = sha1.ComputeHash(newKey);

    // if you replace this base64 version with one of the encoding 
    //   classes this will become corrupt due to nulls and other 
    //   control character values in the byte[]
    var outval = salt + Convert.ToBase64String(result);

    return outval;
}

private static bool CheckHash(string saltedKey, string password)
{
    var outval = CalculateHash(saltedKey, password);
    return outval == saltedKey;
}
Matthew Whited
+1  A: 

most md5/sha1 libraries return hashes base64 encoded in which case varchar would suffice

Jumby