Chapter 3: Managing Sessions – Hacking the Code

Chapter 3

Managing Sessions

Introduction

For many Web sites, the user experience depends on the continuity of the user session. Since HTTP is a connectionless protocol, it is usually up to the Web developer to maintain some form of session persistence. When users browse through a Web site, they usually access multiple pages with different content on each page. Initially they visit one page, browse the contents of that page, then visit another page and continue until they are finished using that Web site. At that point they either close their browser or keep the browser open and continue on to a different Web site. To the user, it appears as one continuous session with the Web site they visit. But behind the scenes, the process is very different.

For each page visit, the browser makes several requests of the server for those elements required to render the page in the browser window. After retrieving those elements, the client closes the connection with the server. When retrieving the next page, the browser repeats the process, but the server has no idea that this request is connected to the previous request.

For that reason, the server needs to issue some kind of unique token that the browser can present with each visit to know which sessions are related. A token is an identifying string that is unique to each session. Using these session tokens, the server can treat a series of disconnected requests as if they were one continuous connection.

A server can assign two basic types of tokens:

 Session tokens

 Authentication tokens

Session Tokens

Session tokens create continuity between HTTP requests. A session token’s life usually begins with the first request a client makes to a server and ends either with the client closing the browser or after a specified timeout period. Session tokens are most often maintained through cookies, either as session cookies or persistent cookies, and are often automatically handled by the server application. The purpose of a session token is to track the entire interaction between client and server to provide the appearance of a seamless connection with the server. Session tokens apply to all requests, including anonymous requests.

Web developers use session tokens to maintain user preferences, track session variables, connect multiple forms, and maintain lists such as items in a shopping cart. Session tokens usually take the form of cookies because they provide an easy method to store information about a client.

Session tokens should be secure, but security is normally not critical for basic session management.

Authentication Tokens

Once a user authenticates to the server, rather than asking for credentials with each request, the server issues an authentication token that the user can submit with each request to prove his or her identity. The authentication token uniquely identifies an authenticated user. It is important to note that the authentication token is almost as sensitive as the user’s actual credentials except that the authentication token has a limited life span.

It is important to distinguish between session and authentication tokens. By keeping them separate, you can better optimize the settings of each and limit exposure to some types of attacks. For example, you can use the built-in ASP.NET state management features for session tokens and use more advanced techniques for authentication tokens.

In order to remain secure, a token must have certain properties. A token must:

 Uniquely identify a client to the server, but not to anyone else

 Not be usable by the client outside the scope of the application

 Not be usable by anyone else

This chapter teaches you how to secure both session and authentication tokens in an ASP.NET application.

Understanding the Threats

The primary threats against token schemes that we address in this chapter are:

 Token hijacking Being able to access another user’s token and potentially gain access to that user’s account.

 Account hopping Manipulating an existing token to gain access to another user’s account.

 Session fixation Providing another user with a known fixed token to authenticate and then gaining access to that user’s session.

 Token prediction Guessing or predicting a valid session token because the token scheme uses a sequential or predictable pattern.

 Token brute-force attacks Discovering a valid session token by submitting all possible combinations within the token’s key space.

 Token keep-alive The process of periodically sending Web requests to keep a session token from expiring; often used with session fixation attacks.

 Token manipulation Modifying a token on the URL or in a cookie to gain unauthorized access to an application.

 Cross-site scripting An attack that involves injecting HTML or script commands into a trusted application with the purpose of hijacking a user’s cookie, session token, or account credentials.

 Information leakage Improper token mechanisms may leak information about users or user configurations.

 Phishing A form of man-in-the-middle attack in which the attacker lures a legitimate user to enter a password through a fake e-mail or Web form designed to look like that of a legitimate Web site.

The trouble with token schemes is that it is very difficult to design one that perfectly assures you that the token is from a legitimate user login and not an attacker. For example, a user might have a valid token, but it could be coming from a hacker who somehow discovered the token. You can determine if the token is coming from the same IP address to which you issued the token, but it might not actually be coming from the user’s computer, because that IP address could be a firewall or shared proxy server. Even if you do determine that the request is coming from the user’s computer, it might not be coming from the user’s login session. And even if you do determine that, it might not be coming from the user’s browser but instead from a Trojan, a virus, or a worm. And if you determine that it is coming from the user’s browser, the user might not have intended to send it but was the victim of a phishing or cross-site scripting attack. Finally, you don’t know if it is even the real user at the other end; it could be a coworker who knows the user’s credentials or who is using the browser session of a user who is out to lunch.

It is entirely possible with our current technology to create a mechanism that almost completely deals with these issues, but then new issues arise of compatibility, user acceptance, deployment, user training, developer training, and infrastructure support. Consequently, most Web applications end up with vulnerable token schemes saved as cleartext cookies sent over an unsecured connection. This chapter demonstrates how to get the most out of cookie security.

Maintaining State

ASP.NET greatly simplifies the process of managing both session and authentication tokens. A developer can use the default ASP.NET session management features or expand on those to provide more robust and secure token management features. In this section, we will cover:

 Designing a secure token

 Selecting a token mechanism

 Using state providers

Designing a Secure Token

Summary: To be secure, a token must meet certain criteria
Threats: Session hijacking, account hijacking, session fixation, information leakage

Web developers have long used session and authentication tokens in their Web applications. However, lack of understanding of session management has led to the compromise of many Web applications. It is important to keep session tokens secure, but it’s even more important to keep authentication tokens secure. For an authentication token to be secure, it should pass a number of tests, as outlined in the following sections.

Is the Token Tightly Bound to the User’s Session?

This is the most common failure of tokens, often allowing attackers to hijack another user’s session by manipulating a token. Most token schemes have no binding to a particular user, relying on the assumption that possession of the token is proof of ownership.

Does the Server Transmit the Token to the Client in a Secure Manner?

Essentially, this means using SSL or IPSec to encrypt the traffic when sending the token. If the traffic is not encrypted, the token could be visible to others. When you use a cookie, it is important to mark it as secure so that the client’s browser handles it properly.

Does the Application Use a Sufficiently Large Keyspace?

To prevent brute-force attacks on the session token, you should use a large enough token to ensure that an attacker cannot easily come across a valid token. For example, a 128-bit number allows for 340,282,366,920,938,463,463,374,607,431,768,211,456 (or 2128) possible unique session tokens.

Does the Application Use a Strong Random-Number Generator for Tokens?

A strong random-number generator ensures that a token utilizes the available keyspace and does so in an unpredictable manner. It is impossible for a computer to generate a truly random number, but many pseudo-random-number generators are good enough that you cannot easily reproduce the same results twice.

Does the Application Create New tokens for Each Login?

A user should never be allowed to reinstate a session with an old token. Each time the user authenticates, always destroy any old tokens and initiate a new session with a new token.

Will the System Accept Any Token Provided by the Client?

If a system accepts a client-provided token, it could be vulnerable to what is called a session fixation attack. With this kind of attack, the attacker sends the victim a URL that contains a session token. The victim clicks the URL and enters his or her login credentials, validating the session. The attacker then browses to the site using that same session ID and gains access to the user’s account. Not only should you reject such tokens, you should also log them as a security incident.

Is It Possible for a User to Manipulate the Token to Hop to Another Account?

Many Web applications use a weak session token that, when modified, allows an attacker to hop to another authenticated user’s account. The token should always be bound to a specific account and session, and you should always avoid sequential or predictable session tokens.

Can the Application Detect a Modified or Invalid Token?

To prevent others from modifying a token to somehow exploit an application, the system should be able to identify a cookie that has been changed from its original state. The application should always be able to identify a token that it did not issue itself or that a user has changed since issuing by using a digital signature or hash of the token.

Does the Token Uniquely Identify the Client?

The token should always be a random number that in no way identifies the client. The token should be a random number generated by the server and stored on the server side in association with the user ID. When the client provides the token to the server, the server looks up that session and identifies the client.

Does the Token Properly Limit Its Scope?

An application should associate a token only within a limited application scope to prevent users from overstepping their bounds or from inadvertently transmitting their tokens across an insecure connection. This is especially important for cookies so that the cookie is not sent when moving from a secure to an insecure page. It also prevents cookie-hijacking attacks from servers with the same domain name.

Does the Token Have Both Relative and Absolute Timeouts?

A session token should be valid for only a short period of time since the user’s last request. Because it is possible for a user to keep a token alive indefinitely, tokens should also have an absolute expiration. At this point, all information regarding this token should be destroyed and a new token issued if the session is still active.

Does the Client Store the Token After the Session Ends?

This is a problem because the user may be connecting from a shared computer. The client’s browser stores persistent cookies, keeps a history of URLs, and caches visited pages, making this a challenge for all types of tokens.

Does the User Have the Option to Terminate a Session?

Although a session should eventually time out, users should also have the option to terminate sessions themselves. When they do so, all information regarding the token should be destroyed. It is also important that you delete any old sessions from that user who may remain in the database.

Can You Revoke a Token from the Server?

Sometimes you need to revoke a single session or even all sessions due to a security incident. The token must rely on the server for its validity. For example, with the built-in ASP.NET session tokens, the server can only determine if it issued the token based on its hash, but because the server does not maintain a list of sessions, there is no mechanism to revoke a token.

Unfortunately, few token schemes meet all these criteria and in fact, the HTTP protocol itself makes it difficult to meet all these perfectly. Every token method has its weaknesses and no single method is perfect. Moreover, many developers trade security for greater compatibility with their wide range of customers.

Security Policies

 Where possible, use extra measures to bind the token to the client session.

 Transmit tokens using SSL whenever possible.

 Always use a sufficiently large keyspace for session tokens.

 Always use a strong random-number generator for session tokens.

 Never accept new tokens submitted by a client.

 Never include visible plaintext user identifiers in the token.

 Always limit the token’s scope to the current application.

 Use both relative and absolute timeouts for tokens.

 Take measures to prevent the client from storing the session token after the session ends.

 Allow users to manually terminate a session.

 Always issue a new token with each session login.

Selecting a Token Mechanism

Summary: Carefully choose a token mechanism that provides users with the best security
Threats: Session hijacking, account hijacking, session fixation, information leakage

URL-Based Tokens

URL-based tokens are tokens that exist on the URL as part of the URI path or as part of the query string, as shown in these examples:

This method is perhaps the most common because it is the most universally compatible. The drawback with this method is that it is easy for others to discover your session token. The token may appear in your browser history, your browser cache, or logs of any intermediary proxy servers or filters and can be sniffed by others, provided that it is not secured via SSL, and it will show up as the referrer when you click links to other sites. One old trick that used to work with many free Web-based e-mail systems is to embed an image link that you control in an e-mail and wait for the person to view the message. Once they do, their session token will appear in your Web logs under the referer[sic] field. All you have to do is enter that URL and you have access to that person’s account.

If you use URL-based tokens, there is also the risk of users sharing, copying, or bookmarking URLs that contain session tokens. Another problem with URL-based tokens is that they are more susceptible to session fixation attacks because it is easier for an attacker to send you a URL that already contains a session token. (Session fixation attacks are explained in more detail later in this chapter.)

Cookie-Based Tokens

Another common method for communicating tokens is using an HTTP cookie mechanism. Most browsers support cookies, but some users block them for privacy reasons or in the belief that they are a security risk. But an increasing number of Web sites require the use of cookies to authenticate to their sites. The argument of privacy is weak because users are already identifying themselves with their credentials.

Cookies are somewhat more secure than other methods because:

 They are sent as part of the body, which is rarely stored in a log file.

 Session cookies do not persist across browser sessions (unless marked as persistent).

 They have built-in mechanisms for expiration, scope, and using secure connections.

However, cookies do not guarantee immunity from session-hijacking attacks. If not sent over an SSL connection, cookies are still vulnerable to sniffing and man-in-the-middle attacks, and the attacker may be able to physically steal the cookie if he has access to the user’s computer. Microsoft introduced an HttpOnly cookie in Internet Explorer 6 Service Pack 1 that stops one specific type of cookie-hijacking attack but does not completely eliminate the problem. Most security problems related to cookies are due to poor security policy and development practices. Later in this chapter, we look at how to properly configure and use cookies for authentication tokens.

ASP.NET forms authentication uses cookie-based tokens, but it also offers the option to use encryption and verification of the session token.

Form-Based Tokens

Some Web sites embed tokens as hidden fields within HTML forms. The browser submits the session token in the request body when moving to a new page. Here is an example of using a token in a hidden form field:

The security of hidden form fields is about the same as cookies, but it does not provide as many features as cookies. Normally this method works best for limited authentication state management, such as using a shopping cart.

ASP.NET provides a form-based mechanism called ViewState that enhances the hidden form field technique to include encryption and a more structured storage. ViewState overcomes some of the problems of form-based tokens, but it still is not a perfect solution. We look at ViewState and some techniques for making it more secure later in this chapter.

The basic obstacle here is that none of these state management techniques was designed for secure token management, nor are they entirely appropriate for that use by themselves. None of these methods provides the robust security and privacy features required for sensitive Web-based applications. To make matters worse, although SSL can compensate for many of these limitations, many large Web sites still fail to use it, and it is very rare to see a Web site utilizing client-side certificates.

Security Policies

 If using URL-based tokens, take extra measures to bind the token to the client session.

 With cookie-based tokens, make sure you follow proper security guidelines.

 Whenever possible, use SSL to help protect authentication tokens.

Using State Providers

Summary: Properly secure the state provider you select
Threats: Session hijacking, account hijacking, session fixation, information leakage

ASP.NET supports three methods for storing session state on the server: in-process, ASP.NET State Service, or SQL Server. The decision to use one method over the other is based on reliability and scalability requirements. In-process state management is similar to what was available with IIS 5 and classic ASP. In-process state management is handled locally within the ASP.NET worker process. It is the fastest of the three, but all session information is lost if IIS or the Web application restarts. The ASP.NET State Service is an external service that maintains state even if IIS restarts, and it can be shared by multiple servers. You can run the ASP.NET State Service locally or on a remote computer. SQL Server state management is the most scalable and reliable solution but the slowest and most difficult to configure.

Securing In-Process State

There is not much to do to customize local state management, but there are a few steps you should take if you are using it. To use in-process state management, edit your web.config file as follows:

If you use in-process state management, be sure to disable the ASP.NET State Service using the Services administrative tool. If you have Windows Server 2003, you can also disable it using this command from a command prompt:

C:\>sc config aspnet_state start= disabled

Securing the ASP.NET State Service

The ASP.NET State Service is essentially a miniature HTTP server that specifically handles session state PUTs and GETs. Figure 3.1 shows a packet capture of the type of traffic ASP.NET sends to the service.

Figure 3.1 ASP.NET State Service Packet Capture

The weakness with the ASP.NET State Service is that it uses an unencrypted and unauthenticated connection to the Web server or with anyone else who can reach the open port. Unfortunately, the service has only a limited facility to restrict IP addresses. In fact, all it can do is allow or block all external connections through an undocumented registry setting. This works well if you use the ASP.NET State Service on only one computer, but it will not work in a Web farm. If you run the service for a single local Web server, you can set that key as follows:

 Key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\

 Value AllowRemoteConnection

 Type DWORD

 Settings 0 to block remote connections, 1 to allow remote connections

To effectively limit access to this service, you must configure an external packet filter or use IPSec rules to restrict (and encrypt) traffic to authorized servers only. Note that you can also use an obscure port for the service to make it more difficult to locate. You can do this with the following registry setting:

 Key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\

 Value Port

 Type DWORD

 Settings Any unused port number from 1 to 65535

TIP

Note that if you change the default listening port, be sure to make the same change in your web.config file.

To configure ASP.NET to use the state service, set the following in your web.config file:

If you want to further obscure the location and port of the state service, you can encrypt the connection string and save it in the registry with the aspnet_setreg.exe (available at http://support.microsoft.com/default.aspx?scid=329290). To use this tool, enter a command such as this:

After that, change the stateConnectionString attribute in your web.config file to this:

Securing SQL Server State Management

To use SQL Server to manage session state, set your web.config file as follows:

You must also configure SQL Server with the appropriate tables to save session state. For more information on how to do this, see http://support.microsoft.com/?scid=317604.

After configuring the SQL Server tables, create a new low-privilege account on the database server dedicated to state management. As with the state service configuration, you can use aspnet_setreg.exe to encrypt your database connection string:

After that, change the sqlConnectionString attribute in your web.config file to this:

For more information on securing database connections, see Chapter 6 “Accessing Data.”

General Settings

The cookieless option tells ASP.NET to add the session token to the URL, rather than using a session cookie. Some flaws with the ASP.NET session tokens might make them inappropriate to use on the URL. ASP.NET will accept any syntactically valid token and therefore is vulnerable to session fixation. It also provides no encryption or hashing to ensure the token originated from the server and that no one has modified it. Cookie-based tokens have the same problems, but they are somewhat easier to control. For session state, ASP.NET uses a session cookie, so the browser should not save this cookie to disk.

The timeout value is the number of minutes to keep the session alive. Keep this number as low as is practical for your application to reduce exposure to session-hijacking attacks.

Security Policies

 If you’re not using the ASP.NET Session State service, disable it from the services admin tool.

 If you’re using the service, set the AllowRemoteConnection registry key if managing state only for the local system.

 If you’re using the state service with multiple servers, use IPSec or another packet-filtering mechanism to limit access to the port.

 Set a nonstandard port with the state service.

 With state service or SQL Server state management, always encrypt the connection string using aspnet_setreg.exe.

 Avoid using cookieless tokens.

 Set short cookie timeouts that are appropriate for your application.

Using ASP.NET Tokens

After establishing a method for tracking and managing state, you can focus on the specifics of state management. Neither cookies nor URLs were designed to securely transport session or authentication tokens, but there are some techniques you can employ to protect them from attack.

Using Cookies

Summary: Cookies were not designed to be secure, but with careful configuration you can help protect them
Threats: Session hijacking, account hijacking, session fixation, information leakage

HTTP cookies are a mechanism to store user state to create the effect of a seamless connection with a Web server. Cookies were designed to handle user preferences and to track session variables, and they do that well. But when it comes to security, some problems do arise. Much of the specification depends on servers, client browsers, and any proxies in between following the rules. Unfortunately, that is not always the case. Furthermore, there is no requirement for the user agent to even accept cookies, although some Web sites will not grant users access if they don’t.

Normally, to save a cookie with ASP.NET, you use the Response. Cookies collection. However, ASP.NET automatically writes some cookies without you writing any code. If you enable session state, ASP.NET creates a session cookie with a unique token for each user who connects to the server. If you use forms authentication with cookies, ASP.NET also adds a forms authentication token to this cookie.

To access these cookies, you can use the Response. Cookies collection. For the forms authentication cookie, the FormsAuthenticationTicket object provides access to the cookie properties as well as other properties associated with forms authentication.

Cookies will always be somewhat vulnerable, but some settings limit that vulnerability. The properties that have security significance are:

 Domain

 Path

 Expires

 Secure

 Value

Cookie Domain

The optional domain property of the cookie determines which servers can access the information in the cookie. If this property is left blank, the browser is supposed to allow access to only the host that issued the cookie. However, you might still want to specify this value.

The cookie domain allows access to all hosts that match the domain name, including all subdomains. In other words, it matches from right to left, so the cookie domain set to example.com would match *.example.com. The domain name must contain a minimum of two dots, to prevent a server from setting the domain to something like .com. Try to be specific as possible on the domain. For example, if you have a host named ro.cker.org and set the cookie domain to .cker.org, the hosts cra.cker.org, atta.cker.org, and just.about.any.script.kiddie. ha.cker.org can all access the cookie. This is important because an attacker might be able to find and exploit flaws on these systems to gain access to cookies from the target system. Figures 3.2 and 3.3 demonstrate how to set the domain on all cookies.

Figure 3.2 Setting the Domain on All Cookies: C#

Figure 3.3 Setting the Domain on All Cookies: VB.NET

In addition to providing a specific host name, you might also want to check the cookie domain you receive. Say, for example, that the host ro.cker.org sets the domain property to ro.cker.org. However, the host ha.cker.org sets the domain property to .cker.org. Now suppose the user browses to ha.cker.org and the server sends a session cookie with this header:

Set-Cookie: ASP.NET_SessionId=nkih5piv3zdeuc55xj2np555; domain=.cker.org.

Now the user browses to ro.cker.org, and the browser sees that the host ro.cker.org matches the domain .cker.org, so it sends back the cookie it received from ha.cker.org:

Cookie: ASP.NET_SessionId=nkih5piv3zdeuc55xj2np555;

The ro.cker.org server does not check the domain and simply accepts the cookie. This kind of flaw may expose the server to a number of attacks, including session fixation, account hopping, and token manipulation. To prevent this type of attack, always check the cookie’s domain, as shown in Figure 3.2 (C#) and Figure 3.3 (VB.NET). Note that you should always run the verification code in Figure 3.4 (C#) and Figure 3.5 (VB.NET) before you set the domain on all cookies, as shown in Figures 3.2 and 3.3.

Figure 3.4 Verifying the Cookie Domain: C#

Figure 3.5 Verifying the Cookie Domain: VB.NET

Cookie Path

Similar to the domain, cookies also have a path property to limit their scope. After passing the domain check, the server checks the cookie path. The path matches from left to right, matching any path and the paths below it. So if you set the path to / (slash), the cookie applies to all paths on the server. A path of /shopping limits the cookie to all items in the shopping directory and all its subdirectories.

It is helpful to set the path as specifically as possible because a browser will send the cookie to all URLs that match the path. By keeping the scope narrow, you can prevent the browser from sending the cookie when it shouldn’t do so, such as when browsing non-SSL pages. It also limits exposure to cross-site scripting attacks if an attacker finds a flaw in one part of the application. For instance, suppose that you have a Web application for members only at www.example.com/protected/ but provide a demo application that anyone can log in to at www.example.com/demo/. Now suppose that someone plays around with the demo and finds a cross-site scripting flaw that allows him to use the demo application to steal all members’ authentication cookies. In this instance, setting a specific cookie path for the protected and demo directories would likely limit exposure to the attack, so someone would need access to the protected directory to exploit the flaw against actual members, whereas everyone has access to the demo directory. Although this is by no means a solution for the cross-site scripting problem, it does limit exposure to the attack.

TIP

Security strategies usually have varying levels of effectiveness. Some techniques are absolutely effective; other techniques mitigate attacks by limiting scope. Yet other techniques serve only to obscure things to slow an attack and discourage less dedicated hackers. Mitigating and obscuring techniques do not provide solid protection, but they do have the effect of improving overall exposure to attack, especially when you combine many techniques.

Always set a specific cookie path, and always check the path on cookies the client sends your application. For forms authentication, set the path attribute for the forms element in the web.config file.

Cookie Expiration

Cookie expiration in ASP.NET is sometimes confusing, but it is important to keep the lifetime of a cookie as short as possible, to limit exposure to cookie hijacking. Cookies have an Expires property that sets a limit on how long the browser should keep the cookie. If the expiration is left blank, the client browser is supposed to discard the cookie when it closes and not save it to disk. Although the expiration date tells a browser when it should stop using the cookie, there is no guarantee that it will remove expired cookies. So, even though this is not a reliable security measure, you should still take advantage of the feature.

When ASP.NET issues a session ID cookie, it does not set an expiration date, so the browser should discard the cookie when it closes. ASP.NET itself handles the expiring and reissuing of session tokens based on the session timeout setting in the sessionState element of the web.config file, as shown here:

ASP.NET also handles the expiration of forms authentication tickets. The cookies for forms authentication tokens will not have an expiration date unless the Web application sets the cookie as persistent. The FormsAuthentication.RedirectFromLoginPage method has a parameter to indicate whether you want to use a persistent cookie or not, like this:

FormsAuthentication.RedirectFromLoginPage(UserName.Text, Persist.Checked)

Note that the FormsAuthenticationTicket class also has an IsPersistent property that you can set. Some Web applications provide users with a “Remember Me” feature so they don’t have to enter their credentials with each visit. This is an unsafe security practice, and you should never provide it on a Web site that stores personal user information or allows users to perform any significant financial, personal, or communication actions.

The FormsAuthenticationTicket class has an Expiration property that indicates when the authentication token expires, but this does not set the expiration date of the cookie itself. The cookie sent to the browser will have no expiration date unless you mark it as persistent. Once ASP.NET detects an old authentication token, it will revoke that token and issue a new one.

Forms authentication provides two settings that control how it handles token expiration: timeout and slidingExpiration. Timeout specifies the number of minutes until a cookie expires. SlidingExpiration, if set to true, updates the cookie’s expiration date with each browser request, providing that at least half the specified time has elapsed. In other words, setting the timeout to 20 and the slidingExpiration to true allows a user to go up to 20 minutes since the last page request before having to authenticate again. Setting slidingExpiration to false requires the user to reauthenticate 20 minutes after the first request.

WARNING

Timeout settings have no effect on persistent cookies. If you set an authentication ticket as persistent, ASP.NET gives the cookie an expiration date 50 years from the time the server issued the cookie—even more reason not to use persistent cookies with forms authentication.

Although it is nice to be able to choose relative or absolute timeouts, it would be nicer to have both. That way you can timeout after a certain amount of idle time but also timeout after a set number of minutes. Without an absolute timeout, if someone leaves his browser open to a page that automatically refreshes, the session will never end. Attackers can also take advantage of this flaw by keeping a token alive for a session fixation attack.

Secure Cookies

A server can mark a cookie as secure to indicate to the client that it should take measures to protect the cookie. Although the RFC does not specify what security measures the browser should take, it usually means only sending the cookie over an encrypted SSL session. Nevertheless, marking a cookie as secure is only a suggestion from the server and the client browser does not have to honor that setting. In theory, the browser should provide the same level of security that the provided when issuing the cookie. Most common Web browsers do honor this setting.

If you protect your Web site with SSL, you should also mark your cookies as secure so that the client will not send them to a non-SSL page on your Web site.

You can configure forms authentication to always use a secure cookie with the requireSSL setting in the web.config file like this:

Note that if you mark the forms authentication cookie as true, you must provide an SSL connection for the entire protected area of the Web application. Otherwise, if the user visits a non-SSL page the browser will not send the cookie and the user will have to authenticate again to create a new FormsAuthenticationTicket and therefore a new cookie. If you require security for forms authentication cookies, make sure the entire protected area is SSL-enabled. Likewise, you may not want to mark session cookies as secure unless your entire Web application is SSL-enabled.

Cookie Values

The most common use for cookies is storing user variables, such as items in a shopping cart or user preferences. However, some developers use cookies to store sensitive information such as usernames, passwords, or even credit card details. You should never store sensitive information in a cookie, and you should always encrypt the other information that is appropriate to store

Security Policies

 Always set a specific domain attribute and check the domain attribute when reading cookies.

 Always set a specific cookie path and check the path on cookies the client sends your application.

 Use short timeouts on session cookies to limit exposure to session-hijacking attacks.

 If you are using SSL, mark cookies as secure.

 Never store sensitive information such as authentication credentials or credit card numbers, and always encrypt what you do store.

Working with View State

Summary: View State can be secure if you use the right settings
Threats: Parameter manipulation, session fixation, information leakage

View State is an ASP.NET feature that allows you to persist form properties when a page posts back to itself. ASP.NET takes the current state of all form controls and stores them as an encoded string in a hidden form field. The risk of View State is that an attacker might be able to view or modify these form values to accomplish a variety of attacks. Fortunately, ASP.NET allows you to protect the View State data with encryption and detect tampering with hashes.

Enabling View State

You can enable View State on the machine, application, page, or control level. It might be tempting to enable View State for your entire site, but you might want to be more selective, to reduce exposure to attacks. Table 3.1 shows how to configure View State at various levels. In general, if you are not using View State for a page or control, you might want to disable it.

Table 3.1

Enabling View State

Scope Configuration
Machine In the machine.config file, set <pages enableViewState = “true”/>.
Application In the web.config file, set <pages enableViewState = “true”/>.
Page In the page source, use <% @ Page enableViewState = “true” %> or in code set Page.EnableViewState = True.
Control In the page source, set the control property EnableViewState = “True”.

Protecting View State

View State appears in the HTML source as a hidden form field, as shown in Figure 3.6. Although it looks cryptic, it is merely using base 64 encoding. You can decode this data and view it graphically using the ViewState Decoder tool shown in Figure 3.7, which is available at http://staff.develop.com/onion/resources.htm.

Figure 3.6 Sample View State Field

Figure 3.7 The ViewState Decoder

Because View State represents a form’s control properties, an attacker would be able to modify the form by modifying the View State properties. For example, an attacker might be able to change a product’s price on an online shopping cart or modify a parameter to perform an SQL injection attack. An attacker might try a number of other attacks such as cross-site scripting, unauthorized file access, or overflowing buffers.

To prevent attackers from manipulating View State, you can include a message authentication code (MAC). A MAC is essentially a hash of the data that ensures its integrity. You can enable the View State MAC on the machine, application, or page level. You can enable the MAC wherever you enable View State with this attribute:

enableViewStateMac=“true”

WARNING

Although many documents recommend not using the View State MAC unless necessary for performance reasons, I advise you always use it. There is a very slight performance hit but not enough that a human would notice. The security benefits far outweigh the performance hit.

The MAC protects you from someone tampering with View State data, discarding the contents if the MAC does not match. However, anyone can still decode and view the View State contents using the ViewState Decoder tool mentioned earlier. To overcome this problem, you can also encrypt the contents of the View State field. You can configure this encryption with the machineKey element in the machine.config file:

The validation attribute can be MD5, SHA-1, or 3DES. The first two of these cause ASP.NET to create a MD5 or SHA-1 MAC hash, but 3DES encrypts the View State contents and creates an SHA-1 MAC. Setting the validation mode to 3DES protects the data from viewing and prevents parameter manipulation, but this still does not make View State completely secure. An attacker could prepopulate a form, save the View State field, and then trick another user into using that form. To prevent this type of attack, ASP.NET allows you to set a unique key for each user that makes the View State data valid for only that user. You can do this in the page_init event by setting the ViewStateUserKey property of the page object. With authenticated users, you can set this to the user’s name or session ID, but with an anonymous user, you should create a random number and save it in the user’s cookie. Figure 3.8 (C#) and Figure 3.9 (VB.NET) demonstrate how to employ the methods mentioned in this section.

Figure 3.8 Securing View State: C#

Figure 3.9 Securing View State: VB.NET

To properly secure View State, you should use encryption, hashing, and unique user keys. You should always disable View State if you are not using it.

Security Policies

 Disable View State unless you specifically use it.

 Where you enable View State, always enable the View State MAC.

 Set the validation attribute in machine.config to 3DES.

 Set a unique View State user key for each user.

Enhancing ASP.NET State Management

ASP.NET state management is convenient, but it does not meet the criteria mentioned at the beginning of this chapter. For example, session and authentication tokens are not tightly bound to user sessions, and ASP.NET will accept any token the client provides. This section explains how to use the built-in state management features combined with client-side cookies to provide an additional layer of security. This section covers:

 Creating tokens

 Establishing sessions

 Terminating sessions

It is important to note that although it will not stop all attacks, using SSL will further enhance the techniques described in this section.

Creating Tokens

Summary: Use additional code to increase token security
Threats: Session hijacking, account hijacking, session fixation, information leakage

The token you use for session management should be a strong random number and should not directly identify a user, and the application should be able to prove that it is authentic.

Random numbers are an important aspect of security, but they must have certain properties to be considered truly random. For instance, the number should not be predictable or reproducible, and it should be large enough to prevent someone guessing it. When ASP.NET creates a session token, it uses a strong random-number generator to create a 120-bit key, which is certainly adequate for most purposes. This session token is stored in a cookie named ASP.NET_SessionId or on the URL if you’re using a cookieless state. This session token is also available through the SessionID property of the Session object.

The only weakness with the ASP.NET session token is that it is just a random number and ASP.NET will accept any token you supply of the correct length, making it vulnerable to session fixation attacks. To compensate for this weakness, we must create a MAC for the session token based on a random number stored with the server’s state mechanism, as shown in Figure 3.10 (C#) and Figure 3.11 (VB.NET).

Figure 3.10 Enhancing the Session ID with a MAC: C#

Figure 3.11 Enhancing the Session ID with a MAC: VB.NET

This code creates a MAC based on the session ID using a random number stored on the server as the key. It saves this MAC in a cookie on the client. When a client request comes in, you again compute a MAC and see if it matches the MAC stored in the client cookie. If the two match, you know it is a valid session ID; otherwise, you abandon the current session and force the user to acquire a new session ID. After a session times out on the server, the key will no longer be available and the MAC based on that key will no longer be valid. If the client sends a cookie for a preexisting session without providing a MAC, the server abandons the session and starts over with a new session ID.

For forms authentication tickets, you don’t need to add our own MAC if you always use encryption and validation using the protection=“All” attribute of the forms element in the web.config file. This ensures that an attacker cannot view or modify the forms authentication ticket. The ticket is encrypted and hashed based on the decryptionKey and validationKey attributes of the machineKey element in the machine.config file. On a default installation, both of these are set to “AutoGenerate,IsolateApps”, which means that ASP.NET will generate random values for these keys. The IsolateApps setting is important because it ensures a unique key for every ASP.NET application on the server. This is especially important on a shared host because without it, one Web site could produce a valid forms authentication ticket for another site on the same server.

TIP

If you explicitly set custom decryptionKey and validationKey values to allow forms authentication in a Web farm, you can still use the IsolateApps setting to ensure unique keys for each application on the server.

Binding to the Client

Once you establish a secure token and send it to the client, you want to make sure that only that client can use that token. Although it is difficult to absolutely prevent someone else from hijacking that cookie, there are some things you can do to limit the scope enough to block many types of attacks.

There are various ways that an attacker can obtain the session token and hijack the user’s session. Some techniques an attacker could use are:

 Man-in-the-middle attack involving ARP or DNS spoofing

 Obtaining the session token on the URL stored in the cache, in a proxy server’s logs, or listed as the referrer in a server’s Web logs

 Gaining access to the client’s system and stealing the browser’s cookie; Trojans fall into this type of attack, too

 Using cross-site scripting to obtain the browser’s cookie or the token in the URL

The reason so few sites bind to the user’s session is that it is difficult to reliably bind to a single session, and even if you can, it does not protect against all types of attacks. You need to not only obtain the token but make it difficult to use that token from another client.

There are several ways that you can create a stronger bind between the session and the client. The most obvious of these is using the client’s IP address. The main problem with this choice is that some ISPs use load-balanced caching proxy servers. This means first that more than one user will have the same IP address and second that a user might not have the same IP address for each request. To overcome this, you might be able to use the first one or two octets of the IP address. So, if the client comes from 192.168.10.58, you can bind the session to the first two octets, 192 and 168. Another method is to bind to the User Agent string provided by the client. However, some proxies randomly change the User Agent string to help protect user privacy, and it is possible for an attacker to discover and fake this string. Yet another option is to bind to the current ASP session cookie. None of these methods is perfect, but using a combination of these certainly provides more security than using none.

Figure 3.12 (C#) and Figure 3.13 (VB.NET) show how to obtain information about the client and store it in the session state. With each request from that client, you compare the client information with the saved information and abandon the session if they do not match. You will likely need to test these to see if they work with your client base.

Figure 3.12 Binding to the Client: C#

Figure 3.13 Binding to the Client: VB.NET

Security Policies

 Always use a strong random-number generator for session tokens.

 Use a token that does not identify the user to anyone but the server.

 Use an additional MAC to ensure the authenticity of a session token.

 When using forms authentication on a shared server, always use the IsolateApps setting of the machineKey element in the machine.config file.

 Save client variables to help ensure binding to the same client.

Terminating Sessions

Summary: Carefully choose a token mechanism that provides users with the best security
Threats: Session hijacking, account hijacking, token keep-alive

At some point a session must end, at which time you should destroy the session token. There are several ways a Web session can end:

 The client closes the Web browser.

 The client keeps the browser open but browses to a different Web site.

 The client browser sits idle long enough for the token to expire.

 The server revokes the token.

When the client closes the browser, the browser discards any session tokens not marked as persistent. However, if the user just browses to a different Web site, the session tokens remain in the browser’s memory. This means that if users browse back to your site, their session tokens may still be valid. This could be a risk on a shared public computer or if the user leaves a browser window open for an extended period of time. To counter this danger, ASP.NET automatically expires a session after a predefined time, usually 20 minutes.

But if the user is on a Web page that automatically refreshes every few minutes, the session never sits idle long enough to expire. An attacker can use this capability with a session fixation attack to keep the token alive long enough to carry out the attack.

The best countermeasures are to provide three different expiration factors:

 After a specific amount of idle time

 After an absolute time since the token was issued

 After a specified number of uses

Using these factors, you could, for example, expire a session after idle for 10 minutes, after two hours have passed since issuing the token, or after 50 hits to your Web site. Under any of these conditions, you should create a new session token or reauthenticate the user. This type of practice is commonly implemented in online banking Web sites.

Since ASP.NET automatically handles idle session timeouts, we can use session state to store the other two variables. Figure 3.14 (C#) and Figure 3.15 (VB.NET) show how to use, store, and check the absolute expiration and hit counter.

Figure 3.14 Expiring Sessions: C#

Figure 3.15 Expiring Sessions: VB.NET

This code checks to see if the current session has an absolute expiration and hit count. If it doesn’t, the code creates one and saves it with the session state. When the next request comes in, it again checks those values until the client exceeds either. At that point, we expire the session and require the user to again authenticate to the server.

Security Policies

 Use timeouts to expire idle sessions.

 Use absolute timeouts to set a limit on active sessions.

 Use hit counters to limit the use of any single token.

Coding Standards Fast Track

Maintaining State

Designing a Secure Token

 Where possible, use extra measures to bind the token to the client session.

 Always transmit authentication credentials using SSL or IPSec.

 Always use a sufficiently large keyspace (at least 120 bits) for session tokens.

 Always use a strong random-number generator for session tokens.

 Never accept new tokens submitted by a client.

 Never include visible plaintext user identifiers in the token.

 Always limit the token’s scope to the current application.

 Use both relative and absolute timeouts for tokens.

 Take measures to prevent the client from storing the session token after the session ends.

 Allow users to manually terminate a session.

 Always issue a new token with each session login.

Selecting a Token Mechanism

 Avoid using URL-based token mechanisms.

 Use cookie-based tokens whenever possible.

 Use HttpOnly tokens with Internet Explorer clients.

 Use SSL whenever possible to protect session tokens.

Using State Providers

 Disable the ASP.NET State Service if you are not using it.

 Use aspnet_setreg.exe to encrypt the state connection string.

 Avoid using cookieless tokens.

 Set short cookie timeouts as appropriate for your application.

Using ASP.NET Tokens

Using Cookies

 Always set a specific domain and path on all cookies.

 Always check the domain and path on incoming cookies to block cookies with the incorrect scope.

 Do not set an expiration on cookies to have them expire when the browser closes.

 If using persistent cookies, use a short expiration date.

 If using SSL, mark cookies as secure to prevent transmitting them over non-SSL connections.

 Never store sensitive information in a cookie, and always encrypt what you do store.

Working with View State

 Disable View State on all pages where you do not specifically use it.

 Wherever you enable View State, also enable the View State MAC.

 Set the validation attribute of the machineKey element in machine.config to 3DES.

 Set a unique View State user key for each user.

Enhancing ASP.NET State Management

Creating Tokens

 Always use a strong random-number generator for session tokens.

 Use a MAC based on a random number to ensure the authenticity of the session token.

 Use the IsolateApps settings to ensure unique keys between applications.

 Use client variables to tightly bind to the client session.

Terminating Sessions

 Use absolute timeouts in addition to idle timeouts to enforce a maximum token age.

 Use hit counters to limit the use of any one token.

Code Audit Fast Track

Maintaining State

Designing a Secure Token

 Is the token tightly bound to the user’s session?

 Does the server transmit the token to the client in a secure manner?

 Does the application use a sufficiently large keyspace?

 Does the application use a strong random-number generator for tokens?

 Will the system accept any token provided by the client?

 Is it possible for a user to manipulate the token to hop to another account?

 Does the token identify the client?

 Does the token properly limit its scope?

 Does the token have both relative and absolute timeouts?

 Does the client store the token after the session ends?

 Does the user have the option to terminate a session?

 Does the application create new tokens for each login?

Selecting a Token Mechanism

 Does the application use a secure mechanism for transferring cookies?

 Does the application use HttpOnly tokens with Internet Explorer clients?

 Does the application use SSL when appropriate?

Using State Providers

 If you’re not using the ASP.NET Session State service, is the service disabled?

 If you’re using the ASP.NET Session State service, does the system use a nonstandard port?

 If you’re using the ASP.NET Session State service, does the system use appropriate controls to limit access to the service port?

 Does the web.config file contain encrypted session state connection strings created with aspnet_setreg.exe?

 Does the application avoid cookieless tokens?

 Does the application use short cookie timeouts?

Using ASP.NET Tokens

Using Cookies

 Does the code set a specific domain and path on all cookies?

 Does the code check the domain and path on incoming cookies to block cookies with the incorrect scope?

 Does the application set the appropriate cookie expiration properties?

 Does the application mark cookies as secure when they’re sent over SSL connections?

 Does the application avoid saving sensitive information in cookies?

 Does the application encrypt all data stored in cookies?

Working with View State

 Does the application disable View State on pages where it is not used?

 Does the application enable the View State MAC when using View State?

 Does the machineKey element in the machine.config file use 3DES as the validation method?

 Does the application set a unique View State user key for each user?

Enhancing ASP.NET State Management

Creating Tokens

 Does the application use a strong random-number generator for session tokens?

 Does the application ensure the authenticity of the session token?

 Does the system use the IsolateApps setting to ensure unique keys between applications?

 Does the application use client variables to tightly bind to the client session?

Terminating Sessions

 Does the application use absolute timeouts in addition to idle timeouts to enforce a maximum token age?

 Does the application use hit counters to limit the use of any one token?

Frequently Asked Questions

The following Frequently Asked Questions, answered by the authors of this book, are designed to both measure your understanding of the concepts presented in this chapter and to assist you with real-life implementation of these concepts. To have your questions about this chapter answered by the author, browse to www.syngress.com/solutions and click on the “Ask the Author” form. You will also gain access to thousands of other FAQs at ITFAQnet.com.

Q: Does SSL add enough security to make it worth the extra load?

A: SSL does not solve all problems, but it does protect the user from many types of attacks, and you should therefore use it whenever possible. On a slow server, SSL can sometimes double the processor load, but it has a less significant impact with a more powerful CPU. Furthermore, you can offload SSL handling with specialized hardware. If your site deals with sensitive user financial, personal, or communications data, utilizing SSL should be mandatory.

Q: Can I create a different machineKey setting in each application’s web.config file instead of a single setting in the machine.config?

A: Yes, this is actually the preferred method for securing a server with multiple users, such as with a Web hosting service. To configure this, use the IsolateApps setting as follows:

Q: Is it more secure to manage and store session state on the client or on the server?

A: It is best to store and manage session state information on both the client and the server. If you rely only on the server, it is easier for an attacker to take over a client’s session without knowing about the client. If you do it only on the client, it is more difficult to protect from session fixation, and there is no way to revoke a user’s session after a security incident.

Q: Why is it so important to use a strong random-number generator for the session token?

A:  The main reason for a strong random-number generator is to prevent guessing or brute-force attacks of the session token. For example, an attacker could create a script to submit thousands of session IDs until it finds a valid session. The stronger the random number, the more difficult it is to find a valid session. To be effective, the random numbers must be large, unpredictable, and evenly spread across the entire keyspace. If you have a 120-bit random number and 10,000 active sessions, you should feel safe knowing that for every active session, 132,922,799,578,491,000,000,000,000,000,000 session IDs are not valid. Even using an automated script, it would very difficult to come across a valid session ID.

Q: I run a Web-based e-mail Web service, and for business reasons I must place the session token on the URL. How can I prevent the client’s browser from sending the URL token in the Referer field when a user clicks an external hyperlink?

A: If you place the session ID on the URL and the user clicks a link, the destination Web site will be able to see the original URL, including the session token, by looking at the HTTP Referer field. Sometimes an attacker can use this flaw to take over a user’s session. The easiest way to prevent this attack is to wrap all URLs with a URL of your own, passing the original URL as a parameter. So, for example, you could end up with something like this:

    Next, have externallink.aspx grab the original URL parameter and forward the client to that site with a client-side redirect. The external site will only see the link coming from externallink.aspx.