As we mentioned earlier in this chapter, a hash is a type of one-way cryptography. Some people refer to hashing as encryption; others feel it’s not strictly encryption because the hash cannot be unencrypted. A hash is a very large number, generated by scrambling and condensing the letters of a string. In this chapter, you’ll use the SHA-1 algorithm. SHA-1 is an acronym for Secure Hashing Algorithm. The "-1" refers to revision 1, which was developed in 1994. SHA-1 takes a string as input and returns a 160-bit (20-byte) number. Because a string is being condensed into a fixed-size number, the result is called a hash digest, where digest indicates a shortened size, similar to Reader’s Digest condensed books. Hash digests are considered to be one-way cryptography because it’s impossible to derive the original string from the hash. A hash digest is like a person’s fingerprint. A fingerprint uniquely identifies an individual without revealing anything about that person—you can’t determine someone’s eye color, height, or gender from a fingerprint. Figure 1-2 shows the SHA-1 hash digests for various strings. Notice that even very similar strings have quite different hash digests.
It’s common, as shown in Figure 1-2, to display a hash as a base-64 encoded 28-character string. This is easier to read than a 48-digit (160-bit) number.
Hash digests are useful for verifying that someone knows a password, without actually storing the password. Storing passwords unencrypted in the database opens two security holes:
If an intruder gains access to the database, he can use the information to later log on to the system using someone else’s username and password.
People often use the same password for different systems, so the stolen passwords might allow the intruder to break into other systems.
Because the password is used solely for authenticating the user, there’s no reason to store the password in the database. Instead, a hash digest of the password can be stored. When the user logs on to the system, a hash digest from the password she types in is created and compared with the hash digest stored in the database. If an intruder somehow gained access to the password table, he wouldn’t be able to use the hash digest to log on to the system because he would need to know the unencrypted password, which isn’t stored anywhere. In the following exercise, you’ll change the employee management system to validate logons using hash digests instead of passwords.[1]
Create a hash digest function
In this exercise, you’ll write a function that returns SHA-1 hash digests. You’ll then use this function to create hash digests for all the passwords in EmployeeDatabase.mdb and store the hash digests in a field named PasswordHash. This field is already in the database, but it’s currently unpopulated. The passwords are currently stored unencrypted in the Password field.
Start Visual Studio .NET, and open the empty project CH01_EncryptionEncryptDatabaseFieldStartEncryptDatabaseField.sln. This project is empty of code, but it has been set up with the database path, import statements, and a shared library module.
Open the module SecurityLibrary.vb in the Visual Basic .NET editor. This module is empty: it’s where you’ll put all your reusable security routines for use in this and other projects. Add the following function to the library:
Namespace Hash Module Hash Function CreateHash(ByVal strSource As String) As String Dim bytHash As Byte() Dim uEncode As New UnicodeEncoding() 'Store the source string in a byte array Dim bytSource() As Byte = uEncode.GetBytes(strSource) Dim sha1 As New SHA1CryptoServiceProvider() 'Create the hash bytHash = sha1.ComputeHash(bytSource) 'return as a base64 encoded string Return Convert.ToBase64String(bytHash) End Function End Module End Namespace
This function is all that is needed to create a hash. It converts a string to an array of bytes and then creates a SHA-1 hash. The result is returned as a 28-character string.
Open MainModule.vb. You’ll now write a routine to store hash digests for all the passwords in the database. Add the following code to the module:
Sub Main() EncryptField("Password", "PasswordHash") End Sub Sub EncryptField(ByVal strSourceField As String, _ ByVal strDestinationField As String) Dim strSQL, strUsername, strPlainText, strCipherText As String strSQL = "Select Username, " & strSourceField & " from Employee" Dim cnRead As New OleDbConnection(G_CONNECTIONSTRING) Dim cnWrite As New OleDbConnection(G_CONNECTIONSTRING) Dim cmdRead As New OleDbCommand(strSQL, cnRead) Dim cmdWrite As New OleDbCommand() cmdWrite.Connection = cnWrite Dim dr As OleDbDataReader 'Open two connections, 'one for reading and the other for writing cnRead.Open() cnWrite.Open() dr = cmdRead.ExecuteReader() 'Loop through the table, reading strings 'encrypting and writing them back While dr.Read strUsername = dr.GetString(0) strPlainText = dr.GetString(1) strCipherText = Hash.CreateHash(strPlainText) strSQL = "UPDATE Employee SET " & strDestinationField & " ='" & _ strCipherText & "' WHERE Username ='" & strUsername & "'" cmdWrite.CommandText = strSQL cmdWrite.ExecuteNonQuery() Console.WriteLine(LSet(strPlainText, 16) & strCipherText) End While Console.WriteLine(vbCrLf & "Press <Enter> to continue>") Console.ReadLine() End Sub
Now press F5 to run the project. It will populate the PasswordHash field and display the results in the console window. The output should look like this:
Verify passwords using a hash digest
Now you will modify the employee management system to verify passwords with the hash digests you just created.
In Visual Studio .NET, open the project CH01_EncryptionEMS StartEMS.sln.
Open the class clsEmployee.vb; find the declaration
Private m_Password As String
and change it to
Private m_PasswordHash As String
In the Create function, find the line that reads
Me.m_Password = CStr(dr("Password"))
and change it to
Me.m_PasswordHash = CStr(dr("PasswordHash"))
In the IsValidPassword function, find the line that reads
If strPassword = Me.m_Password AndAlso Me.m_IsValidUser Then
and change it to read
If Hash.CreateHash(strPassword) = Me.m_PasswordHash _ AndAlso Me.m_IsValidUser Then
Open the form frmAddNew.vb, and double-click the Add button to open the btnAdd_Click event handler. Change the first line of code from
Dim strPassword As String = Me.txtPassword.Text
to
Dim strPassword As String = Hash.CreateHash(Me.txtPassword.Text)
Still in the btnAdd_Click event, find the line of code that reads
strSQL = _ "INSERT INTO Employee ( UserName, [Password], Fullname ) " & _ "SELECT '" & strUsername & "' As Field1," & _ "'" & strPassword & "' As Field2," & _ "'" & strUsername & "' As Field3"
and change it to
strSQL = _ "INSERT INTO Employee ( UserName, [PasswordHash], Fullname ) " & _ "SELECT '" & strUsername & "' As Field1," & _ "'" & strPassword & "' As Field2," & _ "'" & strUsername & "' As Field3"
Press F5 to run the project. You can log on using the username RKing with the password RKing, as shown in the following illustration. Congratulations—you are now checking passwords without storing passwords! Even if an intruder gains access to the database, the password hash digests can’t then be used to log on.
[1] Validating against hashes is a good mechanism to use for an application that opens a database directly. For a client-server application or a Web application, this mechanism does not protect against "spoofing" the server component—where an intruder who knows the hashes constructs a fake client application that submits the hash to the server. However, if an intruder gains access to the list of passwords, they can do less damage if the passwords are hashed.
18.216.27.251