Search code examples
c#asp.netpasswordscryptographyrainbowtable

Passwords hash protection against "Rainbow tables" reverse engineering with password padding


I've come across this article describing the dangers of storing "unsalted" password hashes in the database that could be subject to reverse engineering with the use of so-called "Rainbow tables".

It also comes with this C# code sample that basically requires storing two hash columns in your user passwords database table (instead of a traditional - one.) The issue of such approach for me is that I already have an established database table with unsalted user password hashes, and adding a new column will require restructuring of the database. So before I do that, I was looking for a different alternative and here's what I came up with.

Here's the function that instead of plainly calculating the SHA1 hash on a password pads it with a long sequence of pseudo-random (but consistent) data and then calculates the hash:

byte[] computeSecureHash(string strUserPassword)
{
    //RETURN: = SHA1 byte array on the 'strUserPassword'

    //Make simple junk array based on the password
    ushort v = 117;
    byte[] arrJunk = new byte[24];
    for (int c = 0, i = 0; i < arrJunk.Length; i++)
    {
        v ^= strUserPassword[c++];
        v *= 7;
        arrJunk[i] = (byte)v;

        if (c >= strUserPassword.Length)
            c = 0;
    }

    //Make crypto byte array based on the password
    Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(strUserPassword, arrJunk);
    pbkdf2.IterationCount = 1000;
    byte[] arrCrypto = pbkdf2.GetBytes(128);

    //Pad actual password
    string strUserPassword_Padded = "";

    const string strChars2Use = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`-=[]\\;',./~!@#$%^&*()_+{}|:\"<>?";
    int nHalfArrCrypto = arrCrypto.Length / 2;

    //Left side
    for (int i = 0; i < nHalfArrCrypto; i++)
    {
        strUserPassword_Padded += strChars2Use[arrCrypto[i] % strChars2Use.Length];
    }

    strUserPassword_Padded += strUserPassword;

    //Right side
    for (int i = nHalfArrCrypto; i < arrCrypto.Length; i++)
    {
        strUserPassword_Padded += strChars2Use[arrCrypto[i] % strChars2Use.Length];
    }

    //For user's password "123"
    //the 'strUserPassword_Padded' becomes:
    //"bwDR]_B>H5t-k:eIq?r_wGBWqWfs#tcAE~DQ5?(Pbj#<+Cw:9(r!B[f_.S<pCjn-123b9l3<Sz^D~>G}v)?NuHT4BZ-pI2$W[kW1e4KO\"`rTg3H`}&jmtrFh1J5c72:})tQ"

    //And now compuse SHA1 on the padded password
    SHA1 sha1 = new SHA1CryptoServiceProvider();
    byte[] bytesInputData = System.Text.Encoding.UTF8.GetBytes(strUserPassword_Padded);
    return sha1.ComputeHash(bytesInputData);
}

So my question is, can someone review this code and tell me what are the dangers of doing it this way vs. what the author suggested in his code? In case of my code sample, I'll have to store only one hash in the database instead of two (password hash + salt hash.)


Solution

  • A salt that's solely derived from the password is close to pointless; you've just created a slightly different (but constant) hash function overall. A single rainbow table (albeit a custom one) could be used to target your entire database.

    Furthermore, if a salt is derived from the password then identical passwords show up as identical "password hashes". Easy passwords will likely show up as duplicates - in effect you are generating your own rainbow table.

    The whole point of storing a unique, independently-generated salt for each password is so that every single password is hashed with a unique hash function. Therefore there would be no single rainbow table that could be used across your entire database.