9 Steps to Secure Forms Authentication

The days of Web coders being able to ignore security are gone. Here’s what you need to know — and do — to authenticate users securely.

Beth Breidenbach

October 30, 2009

22 Min Read
ITPro Today logo

asp:feature

Languages: VB

Technologies: Security| Forms Authentication

 

9 Steps to Secure Forms Authentication

The days of Web coders being able to ignore security aregone. Here's what you need to know - and do - to authenticate users securely.

 

By Beth Breidenbach

 

"Secure coding for Web apps?Boring!"

 

"Don't the network guys takecare of this stuff with their firewalls and Intrusion Detection Systems?"

 

"As long as I can code a loginscreen correctly, haven't I met my secure coding requirements?"

 

Does any of this sound familiar? It reflects a commonmind-set. Security tends to evoke images of network perimeters and file accessrules. And let's face it, how many developers really want to know the ins andouts of Access Control Lists (ACLs)?

The truth is, though, the days of Web coders being able toignore security are gone. Modern Web sites expose enterprise-class databases,allow execution of client-side script, and accept input from anonymous users -some of whom are not as friendly as we'd like.

 

This article is not your typical "how to code simple formsauthentication" article. Instead, we'll take a close look at the choices youface at each step in the authentication process, the implications of thosechoices, and how to code them safely.

 

Step 1. Determine Whether to UseForms Authentication

Granted, this is a strange point to make in an articleabout forms authentication. Still, you often code applications the same way outof sheer habit. It's important to remember that the .NET authentication frameworkalso provides excellent support for Passport and Windows authentication.

 

In particular, consider using Windows authentication forintranet and extranet applications. Windows authentication leverages thesecurity of the underlying operating system and scales well, although itrequires you to establish a Windows NT account for each user. It allows theapplication to impersonate the users' Windows NT accounts, operating under thesame access control restrictions as the users themselves. By using Windows authenticationin these scenarios, you avoid recoding authentication and authorizationcontrols that exist already.

 

Step 2. Assess the Level ofSecurity Required

Just as the most appropriate authentication mechanismvaries between applications, the level of security required varies as well.Security has a cost attached to it - programming time, run time overhead, andso on. Take a moment to assess what secrets your Web application needs to guardand in which portions of the Web site those secrets reside.

 

At a high level, you can categorize secrets into two majorcategories: customer and business. Customer secrets are the informationentrusted to you by your users. These secrets might be as simple as a usernameand password, or your users might include identifying data such as credit cardsand e-mail addresses. You have an obligation to ensure your applications don'texpose customers' data to the rest of the world.

 

Consider the simple case of your customer's username andpassword. What is the likelihood that a customer uses the same password foryour site as for his or her e-mail address? Pretty likely. On the other hand,"personalization" data about how the customer would like the Web page displayedis often more innocuous and might not require as much security. Identify thevarious types of information your customer has entrusted to you and determinethe risk level associated with disclosing that data.

 

Business secrets include the line-of-business systems yourorganizations use, the databases that support them, your company's network anddatabase configurations, and more. As with customer information, you need toassess what types of information are accessible and what level of risk isassociated with disclosing that information, then code an appropriate level ofsecurity into your site.

 

The end result of this review should be a brief documentidentifying the types of information stored and the security choices made. Thisdocument can be quite useful six months later when someone asks, "why are weaccepting the performance overhead of encrypting user passwords?" We all knowhow challenging it can be to revisit old decisions and remember the details.Document these decisions.

 

Step 3. Pick a Coding Environment

This exercise has a strong payback because it provides theinformation to code correctly for the environment that you will actuallyinstall - which often is quite different from the development environment.

 

You have many options available within ASP.NET formsauthentication. For example, you can choose to let ASP.NET store the users'credentials for you in the web.config file, or you can place the credentialselsewhere. You can insist on cookies, or you can allow authentication withoutthem. The correct development choices depend on the installed applicationenvironment. Few developers enjoy re-coding portions of an application at thelast minute - it's best to identify the choices at the beginning. The table inFIGURE 1 runs through the choices to consider.

 

Category

Environmental Question

Implications

Scalability

Will you use multiple IIS servers for this application?

All servers need access to the same credential data. Store in database or synchronized config files.

Scalability

Must new user information be available to all servers immediately?

Time delay in synchronization will not be acceptable. Store in database unless few updates occur.

Scalability Performance

Will the registration information change frequently?

File synchronization becomes more difficult. Usually stored in database.

Security Level

Is the user information so innocuous that it requires no security, and are passwords not user-defined?

Encryption overhead might not be required. Clear text data in web.config file might be acceptable. (This is rare.)

Security Level

Do portions of the application allow differing views and functionality depending on the role of the authenticated user?

Implement role-based authorization as second layer of security.

Security Level

Is it critical that all client-server communication be encrypted?

Consider SSL for the entire site.

Performance

Do performance considerations override the need to encrypt all traffic?

Enable SSL for only the login page's folder.

Browser Clients

Are you required to accommodate users who haven't enabled cookies?

Implement cookieless authentication.

User Management

Should the cookies expire?

Configure expiration parameters to correct number of minutes.

User Management

Should the credential information persist beyond browser closure?

Configure persistence. If also requiring expiration, additional coding is required.

User Management

How much user data do you wish to track between pages?

Entire authentication cookie should be less than 4K. If you need more than 4K total, user data must be stored elsewhere.

Content

Does the entire application need to be authenticated?

Portions might be allowed to the anonymous public. Configure root and subfolder web.config files accordingly.

Content

Do all pages need to offer the ability to log in?

Add login link to anonymous pages.

Content

Do all pages need to offer the ability to log out?

Add logout link to anonymous pages, display when user is logged in.

Content

Are there non-.aspx files we need to secure within the target directories?

Optionally map file extensions to ASP.NET ISAPI filer.

FIGURE 1: Here is a laundry list of questions to askyourself when implementing your authentication environment. Making thesedecisions from the beginning will help direct your coding efforts.

 

What should be the end result of this exercise? Irecommend updating the document you created in Step 2 with the informationyou've gathered here. You will have one single document describing theauthentication environment that must be coded, as well as the rationale behindthe choices you made. Now you are ready to code.

 

Step 4. Set Up the web.configFiles

Your ASP.NET Web application's authentication begins withcorrect setup of the web.config files. Each application has at least oneweb.config file, placed at the application's root. You should place theapplication's overall security configurations in this root-level file.

 

If you have determined only a few subdirectories requireauthentication, you still place all security configurations at the root leveland use the appropriate XML element to indicatethat most directories will allow anonymous access. A separate web.config file,containing the appropriate element, is placed ineach directory requiring authentication. FIGURE 2 shows the Solution Explorerview of this article's downloadable example application. As you can see, the"Secure" directory has its own web.config file.

 


FIGURE 2: This ASP.NET application has a Web.config file at its rootthat allows anonymous access to most of the application. The directory named"Secure" has its own web.config file configured to deny anonymous access.

 

The listing in FIGURE 3 shows typical root web.configsecurity elements for an application that allows anonymous access to most ofthe site, requiring authentication only to a subdirectory. You need beconcerned only with the and elements in the file (the full web.config file is available for download at theasp.netPRO Web site; see the end of this article for details).

 

The web.config file in FIGURE 3 shows a configurationappropriate for a single-server Web site with few changes to user credentials.This allows anonymous access to the root Web site, securing only certainsubdirectories.

 

       loginUrl="Login.aspx"   timeout="15"   path="/ "   protection="All">                          password="26E13F4ECF73413D5DC9A99FDDD34C84" />               FIGURE 3: Here is a configuration for a single-serverWeb site with few changes to user credentials. It displays typical web.configsecurity elements.   The mode attribute indicates you are using formsauthentication. The details of how forms authentication should be implementedare determined by the attributes found within the element:   name: The name used for the authenticationticket, which is the cookie storing the user credentials. If you don't specifya name it will default to .ASPXAUTH. This can cause conflicts if more than oneASP.NET application is running on the same server. It's a good practice toalways provide a name for the authenticate ticket. loginUrl: the location of the login form.ASP.NET can automatically redirect requests to this page wheneverauthentication is required. timeout: Specifies the number of minutes afterthe last request is received before the authentication ticket expires. Thedefault value is 30 minutes. Note that if you implement persistentauthentication tickets this timeout value will be ignored. Additional coding isrequired when combining persistence with the ability to time out cookies. Youwill see an example of this code later. path: the path for the authentication ticketcookie. Using this directive can sometimes be problematic as some browsers arecase-sensitive. Most applications use the "/" path to simplify matters. protection: The level of protection to apply tothe authentication ticket. Values may be Encryption, Validation, None, or All.Details of these parameters can be found in the .NET documentation.Essentially, Encryption prevents viewing a ticket's content, while Validationallows ASP.NET to verify that the authentication has not been tampered with.Why would you choose a protection value other than "All"? Well, for exampleyour application may already be encrypting all traffic via HTTPS. You wouldn'tneed the additional overhead of encrypting the ticket at the application level.   You can use the element tostore user credentials directly within the web.config file. This is notrecommended for high-update or multiserver applications.   If you're wondering why the directive for a secure site is allowing anonymous access to the site by specifying, it's because this Web site is configured to requireauthentication for only one subdirectory. That subdirectory has its ownweb.config file with this entry to deny allunauthenticated access:        Step 5. Establish Encryption (Note: For a better understanding of encryption, downloadthe resource list from the asp.netPRO Web site; see the end of thisarticle for details.)   Encryption keys are used for both the decryption andvalidation of the authentication ticket. If the authentication ticket isencrypted (through the protection element setting in web.config's formselement) the correct key must be available to decrypt the data prior toattempting to validate it. If the authentication ticket is to be validated, theserver concatenates a validation key to the ticket, computes a MessageAuthentication Code (MAC), and attaches the MAC to the ticket cookie. When theWeb server re-receives the ticket, it re-calculates the cookie's MAC based onits current contents and compares it to the MAC that was sent with the cookie.   If the contents have been tampered with, the two MACs willnot match up. Unfortunately, if the two IIS servers in a Web farm use differentdecryption or validation keys, the authentication process will fail. BecauseASP.NET's default behavior is to generate unique encryption keys for each Webserver, you need to use the directive to specify aconsistent keyset across the entire Web farm.   You can set the element at theserver level (in machine.config) or at the Web application level (inweb.config). This code is taken from a web.config file:   validationKey="980D2378779BF031E3F575AF3B4E8CF7BA60AF5EC142171BF77FC82FA87D7C5C22F96112A8E19A32C7BAFEED049B1D8ABA32F9B790AEDD3473C4F7B195FFAD31" decryptionKey="0EB739C6431A601B9ED74D724645F5068B81E54F86C71814" validation="MD5" />   As you can see in the previous file, I specifiedencryption keys for both validation and decryption. Let's look at the detailsof each.   You may assign a validationKey attribute either akey value or the term "autogenerate," which is the default value. A validationkey may be between 40 and 128 characters long. The range available for eachcharacter is the normal hex range of 0 through F. For obvious reasons, thedocumentation recommends using the maximum length possible for this key. Notethat this key is also used for validating your application's view state values.   When defining a validation key, you must also define whichencryption algorithm to use when validating the authentication ticket. You dothis by setting the validation attribute to either SHA1, MD5, or 3DES.This same setting is used for validating view state values, with one exception:If you specify 3DES (a.k.a. triple DES encryption), it will be used only forview state; SHA1 will be substituted for 3DES for the validation process.   A decryptionKey attribute is also assigned eitherautogenerate or an encryption key value. The defined value may be either 16 or48 characters long. (Note that this contradicts some errant documentationstating the key may be 40 to 128 characters long.) If the key is 16 characterslong, ASP.NET will use DES encryption. If the key contains 48 characters,triple DES (3DES) encryption will be used.   How do you generate adequately random keys for this configfile? Microsoft Support Services article # Q313091 details how to use the RNGCryptoServiceProviderclass to generate keys of the appropriate length and randomness. This article'scode download provides a working example of this code. FIGURE 4 shows anencryption and validation key generated by this application.  
FIGURE 4: Use the RNGCryptoServiceProvider class to generatesufficiently random keys for your encryption algorithms. The code for this pageis available in the article's download.   Step 6. Collect User CredentialsSafely You doubtless have seen numerous examples of login formsbefore. Unfortunately, many of the older examples were concerned only with explaininghow to get login information into a database query. What was often neglected isthe importance of validating the user inputs before putting them to use.   Inadequate input validation, combined with the use ofdynamically built SQL queries, have opened numerous Web sites to the risk ofSQL Injection, Cross-Site Scripting, and other attacks. A whole class of attackvectors depend upon poor input validation for their success - your applicationshould be defended at every point of user input. Risks from such attacks rangefrom simple user data theft to hackers gaining complete control of the databaseserver. For information about attack vector classes for Web applications, seethe Open Web Application Security Project's Attack Component list at http://www.owasp.org/asac.   As Michael Howard explains in his book Writing SecureCode (Microsoft Press), we must assume user input is malicious unless itcan be proven otherwise. For example, your policy might be that passwords arebetween five and 15 characters long and must have at least one number. If so,your login page should validate that the user's input meets the criteria beforeit ever attempts to use it.   The code in FIGURE 5 uses the Regex object tovalidate user input. The two functions, ValidUID and ValidPWD,are used throughout the authentication routines in this article's codedownload.   Private Function ValidUID(ByValUserID As String) As Boolean    Dim oReg As Regex    ' w meansalphanumeric    ' {1,15} means 1 to 15characters    ' ^ and $ mean tomatch from the beginning to the end of    ' the string    ' (i.e. not a partialmatch, but the whole string)     oReg = NewRegex("^w{1,15}$")     If NotoReg.IsMatch(UserID) Then        ValidUID = False     Else        ValidUID = True    End IfEnd Function  Private Function ValidPWD(ByValPassword As String) _    As Boolean    Dim oReg As Regex    ' S means asciicharacters    ' d means numbers    ' Thus, ValidPWDrequires at least one number somewhere    ' within the string        oReg = NewRegex("^(S{0,14})(d{1,15})(S{0,14})$")     If NotoReg.IsMatch(Password) Then        ValidPWD = False    Else        ValidPWD = True    End IfEnd FunctionFIGURE 5: Use Regex objects to validate user input.   To use regular expressions, you specify a pattern and lookfor either full or partial matches within a string. In the case of the ValidUIDroutine, the match string is simple. The code is looking for a series ofalphanumeric characters with a total length of no more than 15 characters.Regular expressions are incredibly powerful for specifying data patterns andwell worth your while to take the time to learn.   Step 7. Authenticate the UserSafely Having validated user data, the next step is to attempt toauthenticate the user. The exact code will vary depending on many factors,including where you decided to locate the authentication data. Your analysis inStep 3 should help you determine the correct location for your data. The twomost common choices are to store the authentication in either the web.configfile or in a database.   Regardless of the authentication method, the generalprocess is the same. A hash is calculated for the password. The password hashis compared to the stored password hash. If the hashes match, an AuthenticationTicket is created to hold the credentials. The user's request (along with theauthentication ticket) is routed to the originally requested URL.   If you've decided to store the hash in the Web.configfile, the entire authentication process is managed by the FormsAuthenticationclass as shown in FIGURE 6.   Private Sub btnLogin_Click(ByValsender As System.Object, _ ByVal e AsSystem.EventArgs) Handles btnLogin.Click  IfValidUID(txtUserID.Text) And _   ValidPWD(txtPassword.Text)Then      IfFormsAuthentication.Authenticate(txtUserID.Text, _       txtPassword.Text)Then         lblUserMsg.Text =""          ' the"false" parameter indicates that we don't         ' need apersistent cookie         FormsAuthentication.RedirectFromLoginPage(_          txtUserID.Text,False)       Else         lblUserMsg.Text =_          "Unable toauthenticate this userid/password"       End If  Else      lblUserMsg.Text = _       "Unable toauthenticate this userid/password"   End IfEnd SubFIGURE 6: The authentication process is managed by theFormsAuthentication class when you store the credentials in the web.configfile.   The FormsAuthentication.Authenticate method used inFIGURE 6 performs the "hash and compare" operation and determines if thecredentials are valid. If so, it the code then uses the FormsAuthentication.RedirectFromLoginPagemethod to route the user back to the originally requested page. (I'll discussredirection options in more detail in Step 8.)   If your site requires frequent updates to authenticationinformation or if it needs to synchronize authentication across multiple Webservers, you will probably want to store the authentication information in adatabase. It is crucial that you avoid the "string building" dynamic SQL seenin so many examples. Simply put, database interaction should occur through acombination of stored procedures, the command object, and its parameterscollection. FIGURE 7 shows an example of safe database access.   Private Function IsAuthentic(ByValUserID As String, _ ByVal Pass As String) AsBoolean     Dim sPassHash As String   Dim oConn AsSqlClient.SqlConnection   On Error GoToErrorHandler     IsAuthentic = False   ' Get the hashed valueof the password   sPassHash = FormsAuthentication._    HashPasswordForStoringInConfigFile( _    Pass, "md5")    oConn = NewSqlConnection()     ' IMPORTANT: This example uses NT authentication to   ' secure the databasefurther.    oConn.ConnectionString= _    "DataSource=breidenbach;Database=Northwind;" & _    "IntegratedSecurity=SSPI;"    oConn.Open()     ' Create a command object and give it the name   ' of the storedprocedure.    Dim oCmd As NewSqlCommand("AuthenticateUser", oConn)    oCmd.CommandType =CommandType.StoredProcedure     ' Add the UserID andPassword parameters.    oCmd.Parameters.Add("@UID", SqlDbType.VarChar, 15)    oCmd.Parameters.Add("@PWD", SqlDbType.Char, 32)    oCmd.Parameters("@UID").Value = UserID   oCmd.Parameters("@PWD").Value = sPassHash     ' Retrieve the resultof the stored procedure.    ' (1=authentic, 0=notauthenticated)    If iCount = 1 Then      IsAuthentic = True   End If   oConn.Close()Exit Function  ErrorHandler:    ' Don't show thedatabase error to the user because   ' it might discloseinformation useful to an attacker.    Err.Clear()   IsAuthentic = FalseEnd FunctionFIGURE 7: Use a combination of stored procedures, thecommand object, and its parameters collection to create a secure database. Thestrong data typing and delineation between code and data more than justify theextra effort to use command parameters.   A final note on interacting with the user during thisprocess: It is crucial that, when authentication fails, you be careful aboutwhat information you tell the user. In general, the error message should letthem know that an authentication error occurred without giving a hacker enoughinformation to start guessing the correct values. Usually, a generic "unable tomatch the user ID or password" message is sufficient.   Step 8. Create the AuthenticationTicket After authenticating the user's credentials, you mustcreate an authentication ticket. The authentication ticket is a cookiecontaining the user's ID and credentials (I'll discuss cookielessauthentication in Step 9.) The default behavior is for this cookie to expire 30minutes after the last request or when the browser closes, whichever comesfirst. This behavior may be overridden programmatically like this:   ' First parameter is user's name,second is' whether to persist cookie. TicketCookie = FormsAuthentication.GetAuthCookie( _ sUser, True) TicketCookie.Expires = DateTime.Now.AddMinutes(15)   As I noted in the table in FIGURE 1, you can also add alimited amount of user information to the authentication ticket. Most browsersallow a maximum of 4K per cookie, so you must use this capability sparingly. Inthe example shown in FIGURE 6, I have added the user's color preferences to theauthentication ticket. It requires you to create an authentication ticketmanually, then encrypt it and add it to the cookie collection. FIGURE 8 alsoshows how to persist an authentication ticket and use an expiration time.   Dim oTicket AsFormsAuthenticationTicketDim EncryptedTicket As StringDim TicketCookie As HttpCookie  sColor = GetFavoriteColor(sUser)   ' First parameter is version number' Second parameter is cookie name' Third parameter is current date' Fourth parameter is expiration time' Fifth parameter is whether to persist the cookie' Sixth parameter is the user's additional dataoTicket = New FormsAuthenticationTicket(1, _ FormsAuthentication.FormsCookieName, _ DateTime.Now, _ DateTime.Now.AddMinutes(15), _ True, _ sColor)   EncryptedTicket = FormsAuthentication.Encrypt(oTicket) TicketCookie = New _ HttpCookie(FormsAuthentication.FormsCookieName,_ EncryptedTicket) Response.Cookies.Add(TicketCookie) Response.Redirect(FormsAuthentication.GetRedirectUrl(sUser,True)) FIGURE 8: This Figure shows how to create anauthentication ticket with user data, persistence, and expiration time. Notethat you must also add the ticket to the Cookies collection programmatically.   At this point, you have authenticated the user and createdan appropriate authentication ticket whose expiration and persistenceproperties are set to meet the requirements defined for the target installationenvironment. If you wanted user data included in the ticket, it has been addedto the cookie as well. The last step in the process is to redirect the user andticket correctly to the originally requested page.   Step 9. Redirect There are three general redirection scenarios: simple FormsAuthenticationredirection, redirection of a manually created FormsAuthenticationTicket,and cookieless redirection.   In a simple scenario, where you can assume cookies and havenot created your own authentication ticket, redirection is straightforward. TheFormsAuthentication class's RedirectFromLoginPage method routesthe user and ticket to the originally requested Web page automatically. Thefirst parameter identifies the user, and the second indicates whether thecookie should be persisted:   FormsAuthentication.RedirectFromLoginPage(sUser,False)   When redirecting a manually generated ticket, you need toadd it to the Response.Cookies collection and redirect to the requested page:   Response.Cookies.Add(TicketCookie) Response.Redirect(FormsAuthentication.GetRedirectUrl( _ sUser, True))   The third, cookieless scenario, is a bit more complex. Theticket is presented to the requested page by encrypting it and adding it to theURL as the value associated with the cookie's name. (This is the name youassigned in the name attribute of web.config's element.)   The code necessary to implement authentication withoutcookies is shown in FIGURE 9. You use the FormsAuthentication.Encryptmethod to encrypt the ticket. You must examine the requested URL to determinewhether it contains parameters already; adjust the syntax for concatenating theencrypted value accordingly.   Dim oTicket AsFormsAuthenticationTicketDim Encrypted As StringDim TicketCookie As HttpCookie  oTicket = New FormsAuthenticationTicket(sUser, False, 30) Encrypted = FormsAuthentication.Encrypt(oTicket) CallingURL = FormsAuthentication.GetRedirectUrl(sUser, _ False) If CallingURL.IndexOf("?") = -1 Then   CookielessURL =CallingURL & "?" & _   FormsAuthentication.FormsCookieName & "=" & EncryptedElse   CookielessURL =CallingURL & "&" & _       FormsAuthentication.FormsCookieName & "=" & EncryptedEnd IfResponse.Redirect(CookielessURL) FIGURE 9: This shows how to implement cookielessauthentication. You will use FormsAuthentication.Encrypt to encrypt theticket, followed by adding the result programmatically to the URL beforeredirecting to the requested URL.   Hopefully, this article has provided you with a betterunderstanding of the key coding choices in forms authentication and how toassess them. To help you pull the pieces together even further, an exampleauthentication Web application is available for download. It showcases each ofthe authentication choices I discussed in this article.   Beth Breidenbach is a product architect for Getronics, aNetherlands-based provider of software and infrastructure solutions throughoutthe world. A self-professed "data geek," Beth has an abiding interest in allaspects of data design, security, storage, and transmission - which was anatural lead-in to exploring the possibilities inherent in the new family ofXML-related technologies. Beth's most recent project was the application of XMLand database technologies to rule processing engines. E-mail Beth at mailto:[email protected].  Tell us what you think! Please send any comments about thisarticle to [email protected] include the article title and author.   Authentication vs. Authorization It's important to understand the distinction betweenauthentication and authorization. Authentication is the process ofverifying that the credentials presented by the user are valid. Authorizationis the determination of what functions, data, and resources the user should beallowed to access. Thus, authorization always occurs after authentication.   This article does not attemptto address authorization, as it is another subject in and of itself.      

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like