Article
Securing Passwords in Your Database
Page: 1 2
Now, when I add a new user, the password is hashed and the digest is stored within the Pass field in the Users table, as shown below.

See how simple that is? To validate a user, we simply create a login form (login.aspx) complete with two TextBox controls and a Button control to handle the validation. The markup would resemble the following:
<form runat="server">
<strong>Authenticate:</strong><br /><br />
Username:<br />
<asp:TextBox id="txtUsername" runat="server" /><br />
Password:<br />
<asp:TextBox id="txtPassword" runat="server" TextMode="Password" /><br /><br />
<asp:Button id="btnCheck" runat="server" Text="Check Credentials" /><br /><br />
<asp:Label id="lblCheck" runat="server" />
</form>
As a result of this markup, our Login page should look like this:

Once you've got the interface looking the way you want it to, add the OnClick="CheckCredentials_Click" attribute to the Button control as follows:
<asp:Button id="btnCheck" runat="server" Text="Check Credentials" OnClick="CheckCredentials_Click" />
Finally, we create a code declaration block within the <head> tag of our document and add the code to validate our users as follows:
<script runat="server">
Dim objConn As New OleDbConnection("Provider=Microsoft.Jet.OleDb.4.0;Data
Source=C:\Inetpub\wwwroot\PasswordHashing\Database\Customers.mdb")
Dim objCmd As OleDbCommand
Dim objDR As OleDbDataReader
Function CheckCredentials(Username As String, Password As String) As Boolean
objConn.Open()
objCmd = New OleDbCommand("SELECT Username, Pass FROM Users WHERE
Username=@givenUsername", objConn)
objCmd.Parameters.Add("@givenUsername", Username)
objDR = objCmd.ExecuteReader()
If Not objDR.Read() Then
Return False
Else
Dim storedHashedPassword As String = objDR("Pass")
Dim givenHashedPassword As String =
FormsAuthentication.HashPasswordForStoringInConfigFile(Password,
"SHA1")
Return storedHashedPassword = givenHashedPassword
End If
End Function
Sub CheckCredentials_Click(s As Object, e As EventArgs)
If (CheckCredentials(txtUsername.Text, txtPassword.Text) = True) Then
lblCheck.Text = "You entered the right credentials!"
Else
lblCheck.Text = "You entered the wrong credentials!"
End If
End Sub
</script>
Again, don't forget to add the following directives to the top of the page:
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Web.Security" %>
Now, launch login.aspx within the browser and enter a username and password. As you can see from the figure below, when I log in with the right credentials, the Label control displays the message "You entered the right credentials".

Right away you can see the benefits to simplistic hashing. It's far more secure than storing clear text because of the fact that it would be extremely difficult for someone to guess the original password that produced a hashed message digest. Notice I said "extremely difficult". Because we've only performed simplistic hashing, it wouldn't be impossible for a hacker to perform what's called a dictionary attack and exploit vulnerabilities. Again, there is a solution to this problem as well.
Salting a Hash
So far, we've managed to solve the problem of storing clear text passwords in our database in a relatively secure fashion. Unfortunately, this solution is secure enough only to prevent wandering database administrator eyes from seeing usernames and passwords in our database. A hacker can still perform what's called a dictionary attack. Malicious parties may make a dictionary attack by taking, for instance, 100,000 passwords that they know people use frequently (e.g. city names, sports teams, etc.), hash them, and then compare each entry in the dictionary against each row in the database table. If the hackers find a match, bingo! They have your password. To solve this problem, however, we need only salt the hash.
To salt a hash, we simply come up with a random-looking string of text, concatenate it with the password supplied by the user, then hash both the randomly generated string and password together as one value. We then save both the hash and the salt as separate fields within the Users table.
In this scenario, not only would a hacker need to guess the password, they'd have to guess the salt as well. Adding salt to the clear text improves security: now, if a hacker tries a dictionary attack, he must hash his 100,000 entries with the salt of every user row. Although it's still possible, the chances of hacking success diminish radically.
To implement this functionality, we start by adding a new field, called Salt, to our Users database table. The modified design will resemble the below.

When complete, the Datasheet will appear similar to the figure shown below.

Now, we can modify the code within registration.aspx slightly to accommodate the salt. I bolded the code additions to make them easier to distinguish:
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Web.Security" %>
<%@ Import Namespace="System.Security.Cryptography" %>
<html>
<head>
<title>Password Hashing</title>
<script runat="server">
Dim objConn As New OleDbConnection("Provider=Microsoft.Jet.OleDb.4.0;Data
Source=C:\Inetpub\wwwroot\PasswordHashing\Database\Customers.mdb")
Dim objCmd As OleDbCommand
Dim sqlCmd As String
Dim objRng As New RNGCryptoServiceProvider
Dim intSaltSize As Integer = 16
Sub AddCredentials_Click(s As Object, e As EventArgs)
sqlCmd = "INSERT INTO Users (Username, Salt, Pass, Email) VALUES
(@Username, @Salt, @Pass, @Email)"
objCmd = New OleDbCommand(sqlCmd, objConn)
objCmd.Parameters.Add("@Username", txtUsername.Text)
Dim objByte() As Byte = New Byte(intSaltSize) {}
objRng.GetBytes(objByte)
Dim strSalt As String = Convert.ToBase64String(objByte)
objCmd.Parameters.Add("@Salt", strSalt)
objCmd.Parameters.Add("@Pass",
FormsAuthentication.HashPasswordForStoringInConfigFile(strSalt +
txtPassword.Text, "SHA1"))
objCmd.Parameters.Add("@Email", txtEmail.Text)
objConn.Open()
objCmd.ExecuteNonQuery()
objConn.Close()
End Sub
</script>
</head>
<body>
<form runat="server">
<strong>User Registration</strong><br /><br />
Username:<br />
<asp:TextBox id="txtUsername" runat="server" /><br />
Password:<br />
<asp:TextBox id="txtPassword" runat="server" TextMode="Password" /><br />
Email:<br />
<asp:TextBox id="txtEmail" runat="server" /><br /><br />
<asp:Button id="btnAddCredentials" runat="server" Text="Add Credentials"
onClick="AddCredentials_Click " />
</form>
</body>
</html>
Notice the use of the RNGCryptoServiceProvider class. This class is used to create the salt. The GetBytes() method of the RNGCryptoServiceProvider class creates an array of random bytes for cryptographic purposes. Although we could have used the Random class, the RNGCryptoServiceProvider class provides a result that is much more random than that of the Random class.
This time, when I add a new username (zak) and password (zak), the result is written similarly to our first example. The difference here is that, when I add a second user (fred) with the same password (zak), the digest for both the password and salt are completely different across the board. This point is illustrated below.

To validate a user now, we simply modify the CheckCredentials_Click() function within login.aspx as follows:
Function CheckCredentials(Username As String, Password As String) As Boolean
objConn.Open()
objCmd = New OleDbCommand("SELECT Salt, Pass FROM Users WHERE
Username=@givenUsername", objConn)
objCmd.Parameters.Add("@givenUsername", Username)
objDR = objCmd.ExecuteReader()
If Not objDR.Read() Then
Return False
Else
Dim strSalt As String = objDR("Salt")
Dim strStoredPassword As String = objDR("Pass")
Dim strGivenPassword As String =
FormsAuthentication.HashPasswordForStoringInConfigFile(strSalt
& Password, "SHA1")
Return strStoredPassword = strGivenPassword
End If
End Function
This time, when the user logs in, the hashed password is compared to the value of the salt and password combined. As you can see from the image below, I've entered the correct username and password.

Conclusion
Now that you know that the .NET Framework provides excellent support for storing passwords securely, you shouldn't ever have to store clear text passwords within your database ever again!