asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Protecting Passwords with a One-way Hash Function
By Peter Persits
Rating: 4.3 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction


    As a commercial ASP developer, you are almost certain to be given the task of designing a password-protected Web site. When that happens, you will have to make a decision about how to store user password information securely, and then be able to justify your choice to your boss or client.

    Now, what is "secure," anyway? If we store clear-text user login information in a password-protected database, such as SQL Server or MS Access, that gives us a certain degree of security. But what if the password to the database is compromised? Then your entire user password database will be compromised as well. Even if you are quite certain of the security of your database, your users’ passwords are still accessible to you and all system administrators at your company, which to a certain extent violates user privacy. Scrambling the passwords using some home-brewed algorithm (a few character permutations and substitutions perhaps) may add some obscurity but not true "security."

    Another approach would be to encrypt all passwords in your database using some industry-standard cipher, such as the Data Encryption Standard (DES) or RC2. These algorithms require an encryption key, which you would have to keep secret. Although this is much more secure than keeping the passwords as clear text, the same issues persist – if the key is compromised, the entire database is compromised, and user privacy is still not protected.

    If there were an encryption algorithm that did not require a key and produced an irreversibly encrypted cipher-text, all our problems would be solved. Instead of storing user passwords themselves, we would store their one-way encrypted values. When a user logs in, we encrypt the submitted password and compare that with the value stored in the database. If they match, the user is authenticated. Notice that we kill both birds with one stone. Even if the password database were compromised, it would still be hard for an intruder to recover the original passwords, and user privacy is protected.

    Fortunately, such magical algorithms exist, and they are called one-way hash functions.

    One-way Hash Functions

    A one-way hash function has many names. Among them are message digest, fingerprint, and compression function. A hash function is an algorithm that takes a variable-length string as the input and produces a fixed-length binary value (hash) as the output. The tricky part is to make this process irreversible, that is, finding a string that produces a given hash value should be very hard (hence the word "one-way"). It should also be hard to find two arbitrary strings that produce the same hash value.

    Many hash algorithms were invented but few met the tough cryptanalytic requirements. Among them are MD4, MD5 and SHA-1. MD4 and MD5 were invented by Ron Rivest for RSA Security, Inc. and produce 128-bit hash values. SHA-1 (also known as simply SHA) was designed by NIST and NSA and produces 160-bit hash values. SHA-1 is generally considered more secure than MD4 and MD5 due to its longer hash value. To crack a 160-bit hash, you would need to try about 2 ^ 160 = 1.5E48 various strings – an enormous number by any standard.

    To compute the one-way hash function of a string in the ASP environment, you need a third-party component. We will use Persits Software AspEncrypt for our code samples, but there are other commercial and freeware components out there.

    The following code computes and displays the SHA-1 value of a text string:

    
    <%
    	Set CM = Server.CreateObject("Persits.CryptoManager")
    	Set Context = CM.OpenContext("mycontainer", True)
    	Set Hash = Context.CreateHash
    	Hash.AddText "some text"
    	Response.Write Hash.Value.Hex
    %>
    
    
    Here we create an instance of CryptoManager, the main AspEncrypt object, open a cryptographic context object by specifying the key container name (in this case "mycontainer"), and use the context object to create an empty SHA-1 hash. We then supply the text string we want to compute the hash of and display the result in the Hex encoding. We could have chosen the Base64 encoding, instead, by writing
    
    Response.Write Hash.Value.Base64.
    
    
    Either format is acceptable for our purpose, although Base64 is more compact than Hex (28 vs. 40 characters representing a 160-bit hash value).

    Dictionary Attack and Salt

    Even a hash-encrypted password database is not entirely secure. A hacker can compile a list of, say, the 1,000,000 most commonly used passwords and compute a hash function from all of them. He can then get hold of your user account database and compare the hashed passwords in the database with his own list to see what matches. This is a "Dictionary Attack" and it can be very successful. Where can one obtain such a list? Well, the Internet, of course.

    To make the dictionary attack more difficult, salt is used. Salt is a random string that is concatenated with passwords before being operated on by the hash function. The salt value is then stored in the user database, together with the result of the hash function. Using salt makes dictionary attacks practically impossible, as a hacker would have to compute the hashes for all possible salt values. However, salt does not help make attacks on individual passwords any harder, therefore it is important to use passwords that are difficult to guess.

    Our sample user database table users will have the following three fields:

    
    username, char(30), primary key
    salt, char(10)
    password_hash, char(40)
    
    
    to store the usernames, salt values and hashed passwords, respectively. You can use any Open Database Connectivity (ODBC)-compliant database such as MS Access or SQL Server. You will also need to create a system ODBC Data Source Name (DSN) pointing to your database. The code snippets below use the DSN name of "crypto," but you can call it whatever you want, of course.

    Our code will generate random 10-character salt consisting of capital letters A to Z, thus giving us 26 ^ 10 = 1.4E14 possible salt values.

    Creating a User Account

    The following code (also found in the file AddUser.asp of the download for this article) allows you to add a new account to our user database. It generates a random salt value, computes the hash value of the specified password concatenated with the salt, and stores the username, hash value, and salt in the database.

    
    <HTML>
    <BODY BGCOLOR="#FFFFFF">
    
    <h2>Add a New User</h2>
    <%
    If Request("Create") <> "" Then
    	if Len(Request("Password")) >= 8 Then
    		If Request("Password") = Request("Password2") Then
    			' Generate random salt (10 characters)
    			Randomize
    			Salt = ""
    			For i = 1 to 10
    				'65 is ASCII for "A"
    				Salt = Salt & chr(int(Rnd * 26) + 65)
    			Next
    
    			' Calculate Hash of (Password & Salt)
    			Set CM = Server.CreateObject("Persits.CryptoManager")
    			Set Context = CM.OpenContext("mycontainer", True)
    			Set Hash = Context.CreateHash
    			Hash.AddText Request("Password") & Salt
    			HashValue = Hash.Value.Hex
    			Set Hash = Nothing
    			Set CM = Nothing
    
    			' Save username, hashed value and salt in the database
    			set rs = Server.CreateObject("adodb.recordset")
    			rs.open "users", "DSN=crypto;UID=sa;PWD=;", 2, 3
    			rs.AddNew
    			rs("username").Value = Request("Username")
    			rs("password_hash").Value = HashValue
    			rs("salt").Value = Salt
    			rs.Update
    			Set rs = Nothing
    
    			Response.Write "Account was successfully created."
    		Else
    			Response.Write "Password was not correctly confirmed."
    		End If			
    	Else
    		Response.Write "Password must be at least 8 characters."
    	End If			
    End If
    %>
    
    <FORM ACTION="AddUser.asp" METHOD="POST">
    <TABLE>
    <TR>
    	<TD>Username:</TD>
    	<TD>
    	<INPUT TYPE="TEXT" NAME="Username" VALUE="<% = Request("Username") %>">
    	</TD>
    </TR>
    <TR>
    	<TD>Password:</TD><TD>
    	<INPUT TYPE="PASSWORD" NAME="Password"></TD>
    </TR>
    <TR>
    	<TD>Confirm Password:</TD>
    	<TD><INPUT TYPE="PASSWORD" NAME="Password2"></TD>
    </TR>
    <TR>
    	<TD COLSPAN=2>
    	<INPUT TYPE="Submit" NAME="Create" VALUE="Create Account">
    	</TD>
    </TR>
    </TABLE>
    </FORM>
    
    </BODY>
    </HTML>
    
    
    Note that this code does not check for duplicate usernames in the database. Since username is the private key in our table, the uniqueness of usernames will be enforce by the database management system (DBMS) itself.

    The user information in our database will look something like this:

    usernamesaltpassword_hash
    jsmithBXZLUOZXAC89A4C867DE091B91BDD8097B4B32E10A136896C3
    ppersits GBRCMJVLKB4F55BAD506112567E5ADCC4F039C51FBD13870E2
    skingIQGIEEUMRE E6A0ABC96DA35879657689AFBCFE15C696199858

    Authenticating a User

    Finally, we will write code that validates a given username/password pair. The validation algorithm goes as follows:

    1. Look up the given username in the user database. If found, retrieve the corresponding salt and hash values. Otherwise display the User Not Found error and exit.
    2. Concatenate the submitted password with the salt retrieved from the database. Compute the hash value of the bundle.
    3. Compare the value obtained in step 2 with the hash value retrieved from the database. If they match, the user is authenticated.
    
    <HTML>
    <BODY BGCOLOR="#FFFFFF">
    
    <h2>Validate Username/Password</h2>
    <%
    If Request("ValidateIt") <> "" Then
    	' Obtain user record (hashed value and salt) by username
    	set rs = Server.CreateObject("adodb.recordset")
    	SQL = "select password_hash, salt from users where username = '" &_
    		 Request("Username") & "'"
    	rs.Open SQL, "DSN=crypto;UID=sa;PWD=;"
    	If Not rs.EOF Then
    		HashValue = rs("password_hash")
    		Salt = rs("salt")
    
    		' Calculate Hash of specified password + Salt from DB
    		Set CM = Server.CreateObject("Persits.CryptoManager")
    		Set Context = CM.OpenContext("mycontainer", True)
    		Set Hash = Context.CreateHash
    		Hash.AddText Request("Password") & Salt
    		HashValue2 = Hash.Value.Hex
    		Set Hash = Nothing
    		Set CM = Nothing
    			
    		If HashValue = HashValue2 Then
    			Response.Write "Account was successfully validated."
    		Else
    			Response.Write "Invalid Password."
    		End If
    	Else
    		Response.Write "User not found."
    	End If
    End If
    %>
    
    <FORM ACTION="Validate.asp" METHOD="POST">
    <TABLE>
    <TR><TD>Username:</TD><TD><INPUT TYPE="TEXT" NAME="Username">
    </TD></TR>
    <TR><TD>Password:</TD><TD><INPUT TYPE="PASSWORD" NAME="Password">
    </TD></TR>
    <TR><TD COLSPAN=2><INPUT TYPE="Submit" NAME="ValidateIt" VALUE="Validate">
    </TD></TR>
    </TABLE>
    </FORM>
    
    </BODY>
    </HTML>
    
    

    A Few Final Words

    The hash-based password-protection method described in this article has won industry acceptance. Windows NT itself uses one-way hash functions to store password information in the Security Account Manager (SAM). That is why none of the Win32 NetAPI functions will help you retrieve a user password. NT does not store them. It stores hash values.

    This method does have certain drawbacks as well. One of them is that if a password is lost, it’s lost. There is no feasible way to recover it (short of trying all possible passwords until a match is found). If a user loses his/her password, you will have to reset it to a random string and send that string to the user as a temporary passkey.

    The use of salt makes your password database virtually invulnerable to a dictionary attack using a pre-compiled list of hash values, but it does not help at all against an attack against any individual password. That is why it is so important to choose passwords that are unlikely to be in a hacker’s "Pocket Dictionary of Most Common Passwords." Choosing long pass phrases, alternating uppercase and lowercase letters, and using numerals and characters like !@#$%^&* is always helpful.

    About the Author

    Peter Persits is the founder and president of Persits Software, Inc., the vendor of the popular ASP components AspUpload, AspNTUser, AspGrid, AspAccessControl, and AspEmail. He has been developing software for Microsoft platforms for more than ten years. Peter holds a Master's degree in Computer Science from American University in Washington, D.C., and is a Microsoft Certified Solution Developer. Peter Persits currently lives in Arlington, Virginia.

    Download

    You can download the complete source for the sample contained in this article:

    http://15seconds.com/files/000217.zip

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Supporting Products/Tools
    AspEncrypt
    Built around the Microsoft CryptoAPI, AspEncrypt helps you harness all major encryption and hashing algorithms such as DES, Triple-DES, RC2, RC4, RSA, MD5 and SHA1 in just a few lines of code. The component can be used in tandem with AspEmail to send encrypted and signed mail in the industry-standard S/MIME format, or with AspUpload to encrypt files as they are being uploaded. AspEncrypt can also be used to issue and manage X.509 digital certificates.
    [Top]
    AspPDF
    AspPDF is an ASP/ASP.NET component which enables generation and management of documents in PDF format. Features include advanced text formatting, font embedding, form fill-in, images, tables, content and page extraction, document stitching, encryption, digital signatures, and more.
    [Top]
    Other Articles
    Feb 3, 2005 - ASP.NET Mixed Mode Authentication
    In many web applications it is desirable for both intranet users and external parties to be able to seamlessly log onto the system. The problem this raises is that it is not easy to allow intranet users to log in via Windows integrated authentication while also allowing external parties to log in to the same application using standard forms authentication. This article will show you one way to achieve the best of both worlds when it comes to authentication.
    [Read This Article]  [Top]
    Dec 8, 2004 - Designing Role-Based Security Models for .NET
    In this article, Michele Leroux Bustamante discusses authentication, authorization and role-based security in .NET. Along the way, he provides some best practices for implementing role-based security in some typical .NET application scenarios including rich clients, Web applications, and Web services.
    [Read This Article]  [Top]
    May 11, 2004 - SharePoint Security and .NET Impersonation
    When implementing custom components that require access to restricted resources, implicit impersonation must be used. Jay Nathan shows how to create a class that makes using .NET Impersonation a snap.
    [Read This Article]  [Top]
    Mar 10, 2004 - Intellectual Property Protection and Code Obfuscation
    Learn about the execution process of CLR-based programs and how to protect your applications from being easily disassembled back into source code.
    [Read This Article]  [Top]
    Feb 24, 2004 - How to Send Secure Mail in ASP-Based E-Commerce Applications - Part II
    Businesses that utilize encrypted e-mail may find Secure Multipurpose Internet Mail Extensions (S/MIME) to be somewhat restrictive. This article shows how to use security features in PDF as an alternative to S/MIME.
    [Read This Article]  [Top]
    Feb 2, 2004 - Fighting Spambots with .NET and AI
    Bill Gates, in a recent interview, predicted the end of spam by 2006. One of the methods he mentioned involved a challenge only a real live person could handle. Adnan Masood shows how to use AI and .NET to create a user verification scheme that incorporates similar concepts Gates alluded to.
    [Read This Article]  [Top]
    Jan 21, 2004 - Configuring .NET Code Access Security
    Code Access Security (CAS) is the .NET Framework security model that grants code permission to resources based on "evidence" pertaining to the encapsulating assembly. In this article, David Myers examines CAS and explains different configuration methods.
    [Read This Article]  [Top]
    Mar 10, 2003 - Platform Neutral and Transparent Encryption of Sensitive Customer Information
    Zhenlei Cai combines an open source C++ encryption library with SQL Server extended stored procedures to create a platform neutral, transparent encryption solution that resides at the database layer.
    [Read This Article]  [Top]
    Jan 15, 2003 - Exploring Machine.Config - User Security and More
    Christopher Spann offers a .NET configuration tip that should help ease system administrators' fears of security compromise and thus assuage growing developer demand for a .NET environment.
    [Read This Article]  [Top]
    Dec 10, 2002 - Encrypting Cookie Data with ASP.NET
    You don't have to be a cryptography expert or spend lots of money on third-party components to secure sensitive data in .NET. In this article, Wayne Plourde shows just how easy it is to encrypt cookie data using encryption classes in the .NET System.Security.Cryptography namespace.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry