Microsoft Security Wiki

where we think out loud

Recent Changes
Lost and Found
Find References
Highlight Changes

Versions

Versions:

Salted Hash Code2

Return to

4/13/2008 5:51:00 PM - appleberry9
HomePage


Hash a Password Using a Random Salt (VB)

Applies to

  • ASP.NET 2.0
  • VB
  • Server-side

Summary

The purpose of this code snippet is to demonstrate how to implement secure password persistence using a cryptographic hashing algorithm with a randomly generated "salt" (or "nonce") value. Cryptographic hashing algorithms are one-way encryption algorithms used to store sensitive data in a non-readable format. A salt can be used in conjunction with cryptographic hashing to add additional entropy to encrypted values and to protect against pre-computed hash or dictionary attacks on a compromised hash value.

Objectives

  • Protect user credentials
  • Avoid storing user passwords
  • Protect against certain brute-force attacks on a compromised hash value
  • Generate a cryptographically random value for the salt that cannot be predicted
  • Add enough entropy to the password hash to increase the difficulty of a cracking attempt exponentially

Solution Example

        Function CreatePasswordHash(password As String) As Byte()
                   ' Convert the string password value to a byte array
                   Dim passwordData As Byte() = UnicodeEncoding.ASCII.GetBytes(password)


                   ' Create a 4-byte salt using a cryptographically secure random number generator
                   Dim saltData(4) As Byte
                   Dim rng As New RNGCryptoServiceProvider()
                   rng.GetNonZeroBytes(saltData)


                   ' Append the salt to the end of the password
                   Dim saltedPasswordData(passwordData.Length + saltData.Length) As Byte
                   Array.Copy(passwordData, 0, saltedPasswordData, 0, passwordData.Length)
                   Array.Copy(saltData, 0, saltedPasswordData, passwordData.Length, saltData.Length)


                   ' Create a new SHA-1 instance and compute the hash 
                   Dim sha As New SHA1Managed()
                   Dim hashData As Byte() = sha.ComputeHash(saltedPasswordData)


                   ' Optional - add salt bytes onto end of the password hash for storage
                   Dim APPEND_SALT_TO_HASH As Boolean = True


                   If APPEND_SALT_TO_HASH Then
                              Dim hashSaltData(hashData.Length + saltData.Length) As Byte
                              Array.Copy(hashData, 0, hashSaltData, 0, hashData.Length)
                              Array.Copy(saltData, 0, hashSaltData, hashData.Length, saltData.Length)
                              Return hashSaltData
                   Else
                              Return hashData
                   End If
        End Function 

Problem Example

The following code snippet shows password hashing without the use of a salt and using a weaker hashing algorithm.

         ' password is obtained from the user as a VB string
        Dim password As String = Request.Form("password")


        ' Convert the string password value to a byte array
        Dim passwordData As Byte() = UnicodeEncoding.ASCII.GetBytes(password)


        ' Create a new MD5 instance and compute the hash 
        Dim md5 As New MD5CryptoServiceProvider()
        Dim hashData As Byte() = md5.ComputeHash(passwordData)
  • Hash values are vulnerable to pre-computed hash attacks
  • Depending on password value, password hash may also be vulnerable to a dictionary attack
  • MD5 offers less encryption strengtgh than SHA-1 and has recently been "broken" by cryptography researchers

Test Case

The following classes must be included in any project making use of the sample code provided above:

        Imports System.Security.Cryptography
        Imports System.Text

Execute a test encryption and comparison of a salted password hash

using the following test case methods:

        Sub Main(ByVal args() As String)
                    ' Create a Hash and compare to two subsequent hashes
                    Dim hash() As Byte =  CreatePasswordHash("foobar") 
                    Console.WriteLine("Created new salted hash for 'foobar'")            
                    Console.WriteLine("foobar produces same hash:" + ComparePasswordToHash("foobar", hash).ToString())
                    Console.WriteLine("fo0bar produces same hash:" + ComparePasswordToHash("f0obar", hash).ToString())
        End Sub


        Function ComparePasswordToHash(ByVal password As String, ByVal hashData() As Byte) As Boolean
                    ' First, pluck the four-byte salt off of the end of the hash
                    Dim saltData() As Byte =  New Byte(4) {} 
                    Array.Copy(hashData, hashData.Length - saltData.Length, saltData, 0, saltData.Length)


                    ' Convert Password to bytes
                    Dim passwordData() As Byte =  UnicodeEncoding.ASCII.GetBytes(password) 


                    ' Append the salt to the end of the password
                    Dim saltedPasswordData() As Byte =  New Byte(passwordData.Length + saltData.Length) {} 
                    Array.Copy(passwordData, 0, saltedPasswordData, 0, passwordData.Length)
                    Array.Copy(saltData, 0, saltedPasswordData, passwordData.Length, saltData.Length)


                    ' Create a new SHA-1 instance and compute the hash 
                    Dim sha As SHA1Managed =  New SHA1Managed() 
                    Dim NewHashData() As Byte =  sha.ComputeHash(saltedPasswordData) 


                    ' Add salt bytes onto end of the password hash for storage
                    Dim NewHashSaltData() As Byte =  New Byte(NewHashData.Length + saltData.Length) {} 
                    Array.Copy(NewHashData, 0, NewHashSaltData, 0, NewHashData.Length)
                    Array.Copy(saltData, 0, NewHashSaltData, NewHashData.Length, saltData.Length)


                    ' Compare and return
                    Return (Convert.ToBase64String(hashData).Equals(Convert.ToBase64String(NewHashSaltData)))
        End Function 

Expected Result

        Created new salted hash for 'foobar'
        foobar produces same hash:      True
        fo0bar produces same hash:      False

Scenarios

  • Application makes use of a dedicated user account management system and stores passwords
  • Application stores a "Secret Question/Answer" credential for password reset operations
  • Application requires storage of highly sensitive data (social security number, credit card number, etc) but does not need to retrieve that data

More Information

Password hash and salt values should always be securely protected in storage. An attacker able to compromise a specific hash and salt value from a database may succeed in using other types of brute-force attacks against the compromised hash.

In the example given, the four-byte salt would require an attacker to maintain 4.3 trillion values for every given plaintext value. Assuming the victim required passwords of only 4 alphabetical characters in length with no other complexity requirements (a very weak password policy by our standards), defeating a four-byte salt would require the attacker to have a database of 2 x 10^15 precomputed hashes handy. Assuming each of these hashes only required one byte to store, this would require 2 petabytes of storage.

Additional Resources

Attributes

  • Applies To: .NET Framework 2.0, VB
  • Category: Cryptography
  • Author: Jonathan Bailey