Salt Passwords for Security

Add an extra measure of protection to your credentials database by salting password hashes with random values.

Hot Tip

LANGUAGE: C#

ASP.NET VERSIONS: 1.0 | 1.1

 

Salt Passwords for Security

Add an extra measure of protection to your credentials database by salting password hashes with random values.

 

By Jeff Prosise

 

In a recent edition of this newsletter (Encode User Input Before Outputting it to a Page - Or Else!), I recommended that when using a database as a credentials store for forms authentication, you store password hashes instead of raw (or even encrypted) passwords. I also demonstrated how to use the FormsAuthentication.HashPasswordForStoringInConfigFile method (try saying that 10 times fast!) to create password hashes.

 

Storing password hashes instead of passwords is a terrific idea, but the truth is you can do better still. Even a credentials store that stores password hashes is vulnerable to dictionary attacks - that is, attacks that use brute force to guess passwords using every word in the dictionary. Let's say someone steals your entire credentials database. To find out if the database contains an entry with a password field that contains a hash for "super," the attacker could use a query similar to this:

 

SELECT UserName FROM Credentials WHERE

 [email protected] -- @hash=SHA-1 or MD5 hash of "super"

 

By performing, say, 10,000 queries using 10,000 words from an electronic dictionary, the attacker could check for 10,000 different passwords and in all likelihood hit upon at least a few that work. Storing hashes rather than real passwords makes the attacker work a little harder, but the credentials database is vulnerable nonetheless. (Note that you can defeat dictionary attacks by enforcing strong password policies, but most users won't choose strong passwords unless forced to do so.)

 

Here's a handy solution that will add an extra measure of protection to your credentials database: Salt the password hashes with random values. That is, before hashing a password, append a string of random characters, or "salt," to it (use a different random string for every password) and hash the salted password. Now dictionary attacks become computationally more expensive because to check the database for a password containing a hash of "super," the attacker would nominally have to run one query for every possible combination of the word "super" and the random number it might be salted with.

 

How do you generate salted passwords hashes? Here's a routine that takes a password as input and returns a salted hash. The salt is a highly random 128-bit value generated by the .NET Framework's RNGCryptoServiceProvider class. The returned string includes both the hash and the base-64-encoded form of the salt used to generate it:

 

string CreateSaltedPasswordHash (string password)

{

    // Generate random salt string

    RNGCryptoServiceProvider csp =

     new RNGCryptoServiceProvider ();

    byte[] saltBytes = new byte[16];

    csp.GetNonZeroBytes (saltBytes);

    string saltString = Convert.ToBase64String (saltBytes);

    // Append the salt string to the password

    string saltedPassword = password + saltString;

    // Hash the salted password

    string hash =

     FormsAuthentication.HashPasswordForStoringInConfigFile

         (saltedPassword, "SHA1");

    // Append the salt to the hash

    string saltedHash = hash + saltString;

    return saltedHash;

}

 

You also need a routine that takes a password and a salted hash and indicates whether they equate. Here's that routine:

 

bool ValidatePassword (string password, string saltedHash)

{

    // Extract hash and salt string

    string saltString = saltedHash.Substring

      (saltedHash.Length - 24);

    string hash1 = saltedHash.Substring

      (0, saltedHash.Length - 24);

    // Append the salt string to the password

    string saltedPassword = password + saltString;

    // Hash the salted password

    string hash2 =

     FormsAuthentication.HashPasswordForStoringInConfigFile

         (saltedPassword, "SHA1");

    // Compare the hashes

    return (hash1.CompareTo (hash2) == 0);

}

 

To validate a password, take the user name that the user typed, query the database for the corresponding salted password hash, and pass both the salted password hash and the password that the user typed to ValidatePassword. A return value equal to true means the password is valid; false means it is not.

 

Do salted password hashes afford absolute protection against dictionary attacks? Not by a long shot. If the salt values are stored in the database (and yes, that's typically how it's done) and the attacker knows HOW they're stored, he or she can build a query that extracts all the salt values and uses them to narrow the range of possible salts. The larger the database, the more protection you receive because more entries means more salt values and thus more queries. If your site stores hundreds of thousands of passwords, dictionary attacks against a salted password database might be too expensive to be worthwhile. If it stores just a few hundred passwords, however, be doubly sure to safeguard that database. And remember: When it comes to resisting dictionary attacks, there's no substitute for strong password policies!

 

Jeff Prosise is author of several books, including Programming Microsoft .NET (Microsoft Press). He also is a co-founder of Wintellect (http://www.wintellect.com), a software consulting and education firm that specializes in .NET. Contact Jeff at [email protected].

 

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish