In Part 1 we looked briefly at the motivation behind using encrypted passwords and examined different classes of encryption algorithms, including MD5. In this part we'll examine how to implement encrypted passwords using MD5!
Using MD5 To Store Encrypted Passwords
Earlier I mentioned that most sites that support user accounts do so by using a database table named something like UserAccount, with fields like UserName and Password. In the case where the password is saved as plain-text, both the UserName and Password fields are of type varchar. However, if we plan on using encrypted passwords we need to change the type of the Password field from a varchar to a binary type of length 16. This change is needed because the encrypted version of the user's password will be a 16-element array of bytes.
Now, whenever a user creates an account, we need to be certain to store the encrypted form of the password they selected into the database. The following ASP.NET code provides a sample Web page for creating a user account. It prompts the user for their username and password and stores these values into a UserAccount database table. However, instead of storing the password as-entered by the user, it first encrypts it using the MD5 code we examined earlier, and then saves to the database this encrypted version.
<%@ Import Namespace="System.Security.Cryptography" %> <%@ Import Namespace="System.Text" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <script runat="server" language="VB"> Sub CreateAccount(sender as Object, e as EventArgs) '1. Create a connection Const strConnString as String = "connection string" Dim objConn as New SqlConnection(strConnString) '2. Create a command object for the query Dim strSQL as String = _ "INSERT INTO UserAccount(Username,Password) " & _ "VALUES(@Username, @Password)" Dim objCmd as New SqlCommand(strSQL, objConn) '3. Create parameters Dim paramUsername as SqlParameter paramUsername = New SqlParameter("@Username", SqlDbType.VarChar, 25) paramUsername.Value = txtUsername.Text objCmd.Parameters.Add(paramUsername) 'Encrypt the password Dim md5Hasher as New MD5CryptoServiceProvider() Dim hashedBytes as Byte() Dim encoder as New UTF8Encoding() hashedBytes = md5Hasher.ComputeHash(encoder.GetBytes(txtPwd.Text)) Dim paramPwd as SqlParameter paramPwd = New SqlParameter("@Password", SqlDbType.Binary, 16) paramPwd.Value = hashedBytes objCmd.Parameters.Add(paramPwd) 'Insert the records into the database objConn.Open() objCmd.ExecuteNonQuery() objConn.Close() 'Redirect user to confirmation page... End Sub </script> <form runat="server"> <h1>Create an Account</h1> Username: <asp:TextBox runat="server" id="txtUsername" /> <br />Password: <asp:TextBox runat="server" id="txtPwd" TextMode="Password" /> <p><asp:Button runat="server" Text="Create Account" OnClick="CreateAccount" /></p> </form> |
Note that the above code sample imports a number of namespaces. These namespaces are imported to save typing (for example, if the System.Security.Cryptography namespace were not imported, when referring to the MD5CryptoServiceProvider class, the code would have to appear as: System.Security.Cryptography.MD5CryptoServiceProvider). The code provides the user with two TextBoxes, one for the username and one for the password. Once the user has supplied these values and clicked the "Create Account" button, the CreateAccount event handler will be executed, and the database table UserAccounts will have a new row added representing the new user.
The screenshot to the right shows the values in the UserAccounts table after some users have been created. Note that the password contains a 16-element binary array, representing the encrypted password. Clearly if someone were to be able to examine the UserAccounts table they could not deduce the plain-text password of any of the users.
Using MD5 To Authenticate a User
Since we are storing the passwords in encrypted form, and since, by the nature of a one-way encryption algorithm it is impossible to retrace from the encrypted form to the plain-text form, you may be wondering how in the world we'll authenticate a user. That is, when a user wants to login and supplies her username and password, how will we know if she provided the correct password?
Recall one of the properties of MD5 - that for any plain-text input the encrypted version of the input will be the same, always. That is, if we use MD5 to generate an encrypted form of the plain-text string "my password", the encrypted version of this will be the same today and forever more. Therefore, to authenticate a user we can simply take the password they provide, encrypt it using MD5, and then see if that encrypted form exists in the database (along with their username). The following code performs this check, logging in a user:
<%@ Import Namespace="System.Security.Cryptography" %> <%@ Import Namespace="System.Text" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <script runat="server" language="VB"> Sub Login(sender as Object, e as EventArgs) '1. Create a connection Const strConnString as String = "connection string" Dim objConn as New SqlConnection(strConnString) '2. Create a command object for the query Dim strSQL as String = "SELECT COUNT(*) FROM UserAccount " & _ "WHERE Username=@Username AND Password=@Password" Dim objCmd as New SqlCommand(strSQL, objConn) '3. Create parameters Dim paramUsername as SqlParameter paramUsername = New SqlParameter("@Username", SqlDbType.VarChar, 25) paramUsername.Value = txtUsername.Text objCmd.Parameters.Add(paramUsername) 'Encrypt the password Dim md5Hasher as New MD5CryptoServiceProvider() Dim hashedDataBytes as Byte() Dim encoder as New UTF8Encoding() hashedDataBytes = md5Hasher.ComputeHash(encoder.GetBytes(txtPwd.Text)) Dim paramPwd as SqlParameter paramPwd = New SqlParameter("@Password", SqlDbType.Binary, 16) paramPwd.Value = hashedDataBytes objCmd.Parameters.Add(paramPwd) 'Insert the records into the database objConn.Open() Dim iResults as Integer = objCmd.ExecuteScalar() objConn.Close() If iResults = 1 then 'The user was found in the DB Else 'The user was not found in the DB End If End Sub </script> <form runat="server"> <h1>Login</h1> Username: <asp:TextBox runat="server" id="txtUsername" /> <br />Password: <asp:TextBox runat="server" id="txtPwd" TextMode="Password" /> <p><asp:Button runat="server" Text="Login" OnClick="Login" /> </form> |
Limitations of Storing Encrypted Passwords in the Database
Before you decide whether or not to employ encrypted passwords in your next project, there are a few limitations to be aware of. First, realize that since the passwords are encrypted, there is no way to determine what a user's password is! While this is exactly what we were after by encrypting passwords in the first place, it means that you cannot provide users with a "Click here to have your password emailed to you" feature. Rather, if the user forgets his password he'll have to have his password reset to some random password, and then be emailed that new, random password. Essentially we cannot email the user his forgotten password because there's no way to determine what, exactly, his password is!
Also, converting from a plain-text password system to an encrypted system is possible, but can be a bit difficult. Essentially, you need to create a new table with the Password field being of type binary and of length 16. Next, you have to use an ASP.NET Web page or a .NET program to read the contents of the existing user database, and for each record, add it to the new table making sure to encrypt the user's password using MD5. Be careful not to delete the old user's account information until you're certain that you copied over their information correctly!
Important Security Note! |
The hashing technique discussed in this article is susceptible to dictionary attacks. A much more secure approach is to salt the hash in some manner. For a thorough discussion on what salting is and why it's an important precaution, be sure to read: Could you Pass the Salt? Improving the Security in Encrypting Passwords using MD5. |
Conclusion
In this article we looked at how to use MD5 to provide encrypted passwords in a database. The .NET Framework contains an MD5CryptoServiceProvider class that provides MD5 encryption functionality. Recall that MD5 is a one-way encrypting algorithm, meaning that it can be used to encrypt a plain-text string to an encrypted form, but not back the other way around. Encrypted passwords make sense for applications where one can do much damage by discovering a user's password, but due to their limitations, may not be suitable for less sensitive applications.
No comments:
Post a Comment