Session Fixation & Forms Authentication Token Termination in ASP.NET

ASP.NET applications commonly have one or more vulnerabilities associated with the use of ASP.NET_SessionId cookies and forms authentication cookies. This article briefly discusses those common vulnerabilities and explains one method of mitigating them in an ASP.NET MVC 4 application. Explanation of the exploits are not included, but I linked many of the keywords to OWASP or MSDN articles to provide more details. The security best practices for session cookies and use of sessions in general are provided in the OWASP Session Management Cheat Sheet.

Background
ASP.NET_SessionId cookies and forms authentication cookies can be used alone or together to maintain state with a user’s browser. Each cookie works a little bit differently. The ASP.NET_SessionId cookie value is an identifier used to look up session variables stored on the server-side; the cookie itself does not contain any data. The forms authentication cookie, named .ASPXAUTH by default, contains encrypted data, stored only on the client-side. When it is submitted in a request to the server, it is decrypted and used by custom application code to make authorization decisions.

ASP.NET_SessionId Alone: Session Fixation
There are three common ways to use these cookies that result in risk. First, when the ASP.NET_SessionId cookie is used alone, the application is vulnerable to session fixation attacks. The root cause of this vulnerability is that the ASP.NET_SessionId cookie value isn’t changed or regenerated after users log in (or cross any kind of authentication boundary). In fact, Session IDs are intentionally reused in ASP.NET. If an attacker steals an ASP.NET_SessionId prior to a victim authenticating, then the attacker can use the cookie value to impersonate the victim after he or she logs in. This gives the attacker unauthorized access to the victim’s account.

Forms Authentication Cookie Alone: Can’t Terminate Authentication Token on the Server
Second, when a forms authentication cookie is used alone, applications give users (and potentially attackers) control over when to end a session. This occurs because the forms authentication ticket is an encrypted set of fields stored only on the client-side. The server can only request that users stop using the value when they log out. The ASP.NET framework does not have a built-in feature to invalidate the cookie on the server-side. That means, clients (or attackers) can continue using a forms authentication ticket even after logged out. This allows an attacker to continue using a stolen forms authentication token despite a user logging out to protect him or herself.

Loosely Coupled ASP.NET_SessionID and Forms Authentication Cookies: Still Vulnerable
Lastly, applications can combine both strategies and use forms authentication and sessions. In this arrangement, the forms authentication cookie is used for authentication and authorization decisions, and the session cookie is used to store additional state information. I’ve seen a 50/50 split between applications retrieving identity information from session variables versus the forms authentication ticket however. Either way, the ASP.NET framework does not explicitly couple a specific forms authentication cookie to an ASP.NET_SessionId. Any valid forms authentication cookie can be used with any other valid session cookie. Depending on the implementation, this results in a session fixation vulnerability (for the ASP.NET_SessionId cookie), the inability to terminate authenticated sessions on the server side (for the forms authentication cookie), or both vulnerabilities.

One Possible Solution: Tightly Couple ASP.NET_SessionIDs to Forms Authentication Identities
There are a variety of potential solutions to mitigate these risks. One of those solutions is to use both the ASP.NET_SessionId cookie and a forms authentication cookie, AND to tightly couple them using the user’s identity as the link. In an application that uses forms authentication, this means that the identity of the user is stored in session variables (you have to do this manually) AND the forms authentication ticket (occurs through the normal use of forms authentication). Then, on each request to the application, the identity associated with each cookie should be compared. If they do not match, invalidate the user’s session and log them out.

This solution prevents session fixation by ensuring that an ASP.NET_SessionId cookie (vulnerable to session fixation) MUST be coupled the user’s own forms authentication token (not vulnerable to session fixation) rather than just any individual’s forms authentication token. Additionally, it allows forms authentication tokens to be indirectly invalidated on the server-side by destroying the session that is associated with it. Since both cookies have to be present and linked by the user’s identity, each protects against the weaknesses of the other.

Solution Implementation in an ASP.NET MVC 4 Application
I created an ASP.NET MVC 4 application to test this solution and to be able to demonstrate code for it. My goal was to create a global function that would execute for every controller and action to ensure the user identity referenced in session variables matched those stored by the forms authentication ticket. For unauthenticated users, the session should not reference a user identity at all. If either of these two conditions are violated, the user is logged out, their session is destroyed, and they are redirected back to the login page.

I wrote CoupleSessionAndFormsAuth.cs, an MVC Action Filter Attribute, to accomplish the validation. The comments in the code explain the filter’s goals and how it works.

CoupleSessionAndFormsAuth.cs:
https://gist.github.com/sekhmetn/5629190


Next, I added the filter to the global filters list, within Global.asax.cs, so it would be executed for every controller and action.

Global.asax.cs:
https://gist.github.com/sekhmetn/5629201


Lastly, I added one line of code to the AccountController to ensure the user’s identity was added to session variables when the user logs in.
https://gist.github.com/sekhmetn/5629290

0 comments:

Post a Comment