I manage the design and development of asp.net application projects. Quite a few of our projects go through third party code reviews and security/penetration testing. Some time back, a security report on one of my projects mentioned that our site was vulnerable to session replay and session reuse.
We investigated this aspect thoroughly and did some exhaustive testing. During this process, we learned a lot of useful things which I will share in this post. The most relieving and rather lucky outcome was that our site was not really compromised in the sense that our application did not reveal any data while simulating session replays. Our application simply threw exceptions when we tried to simulate session replays. This was a case of serendipity at its very best! We had somehow ended up mitigating this security flaw without doing it the right way.
Our asp.net web application used the typical Forms Authentication module, which many of us take for granted. First, a bit of background in case you are just getting your feet wet on this so-called “Forms Authentication”. Asp.Net basically offers you a number of pre-built modules that if you configure and use correctly can save you from writing reams and reams of code. Think of it as pre-packaged components in an assembly line scenario. While you can see the obvious advantages (someone has done all the hard work and tested it), there are some obvious disadvantages too (you become a click-and-configure” developer rather than understanding the logic under the covers).
So if you have a web site asking for a user name and password, Forms Authentication can be used. You can authenticate the credentials entered against any data store (your proprietary database or a simple text file or even your config file) and then use the Forms Authentication module to issue you an “Authentication Cookie”. This cookie will contain a ticket with a variety of useful information. You typically store the username (not the password) and some other useful information in this ticket. You also make some configuration settings in your web.cofig file instructing your application to use Forms Authentication and to redirect users to the login page in case they are not authenticated. So asp.Net intercepts every web request, hands it off to this Forms Authentication module and this module will simply set a property to indicate if the user is authenticated or not. It is really up to you to decide if you are going to allow unauthenticated users access to your web site. Every request that hands off a valid authentication cookie to the server is considered authenticated and the application can then have a common piece of code in a base page that can check if the user is authenticated or your application can use another pre-built component called the Authorization module to direct the users to the login page if they are not authenticated. In the latter case, you do not even have to write any code in a common location to check if the user is authenticated. Now when the user legitimately signs out, you will write code that removes the Forms Authentication cookie from the client browser. This is how Forms Authentication module is typically used.
Now there is a fundamental concept that you should be aware of. The Forms Authentication cookie is completely stored on the client side on the user’s machine. The server has no record of which cookie it issued to which user! Let me try to illustrate this with a simple analogy, albeit contrived, that will serve to explain this concept clearly. Imagine that there is a big theatre and that visitors will have to present some kind of a valid ID (say their driver’s license) to gain access to the theatre. A security personnel checks each visitor’s driver’s license and hands them a paper ticket containing a long series of numbers (cookie). There is a timeout attached to this ticket. Now the first flaw is that once each user gets a valid paper stub, they can simply exchange it with somebody else and gain access to the theatre! The person at the door of the theatre just needs a valid ticket that is not timed out, that’s all. There is no inbuilt mechanism to check if “your” paper stub is actually the one corresponding to your driver’s license.
The next biggest flaw is that if you were to somehow pass this ticket (cookie) to another person through some clever means, that other person can also gain access to the theatre. This can happen in several ways. Let’s say that the theatre has a big “Exit” (your log out). When you pass this “Exit”, a security personnel destroys your ticket. But let’s say that you did not go through the “Exit” and that you simply jumped over a fence (closed the browser) clutching your ticket. Now you can hand this ticket to someone else who can now visit the theatre!
Let us now examine the technical nuances related to the above. ASP.Net uses two cookies to take care of sessions and authentications. One is ASP.Net_SessionID which is established when a client (browser) accesses the web application. This is just an identifier used by the client to store data on the server side as we know that HTTP is a stateless protocol. This is how the server keeps track of a client between successive requests. The other cookie is the Forms Authentication cookie which is named .ASPXAUTH by default. Now the ASP.Net_SessionID cookie is vulnerable to Session Fixation which means that a clever attacker can write some XSS (Cross Site Scripting) and obtain the Session ID. The Authentication cookie on the other hand is not vulnerable to session fixation or client side manipulation. But the flaw as I mentioned earlier is that the server has no record of which user is issued which cookie. The recommended solution to handle this security flaw is to tightly couple these two cookies by using the user id (a unique ID) as the link. This will help to tie a user’s Session ID to his or her Authentication ticket thus ensuring that you cannot mix and match Session ID’s and Authentication tickets.
Basically after a user successfully authenticates, store his unique ID in his session variable. When issuing the Authentication ticket, store this user id also in the ticket. Encrypt the ticket as you would typically do and issue the Forms Authentication ticket to the client (browser). Now, write a couple of lines of code in your base page that will check if the user id in the session matches the user id in the Forms Authentication ticket. That’s all!
Now an attacker has to obtain both the session id and the corresponding authentication ticket of a victim in order to impersonate the victim. The session id is vulnerable to session fixation, but not the cookie. The cookie is vulnerable to the fact that it is not really invalidated on the server side, but the session id is not. Once a user logs out all his session information is destroyed. By tightly coupling both these cookies via the user id, we can protect our site from authentication vulnerabilities.
Please refer to the link http://blog.securityps.com/2013/06/session-fixation-forms-authentication.html and http://www.mcafee.com/uk/resources/white-papers/foundstone/wp-asp-net-forms-authentication.pdf for more information.
At the beginning of this post, I mentioned that our site was not really compromised though we had not followed the above recommendation. What happened was this: – Our application was a typical LOB application and we had stored the user’s role and quite a few other details in the session variables after a successful authentication. We had core logic in the base page that relied on these session variables to dynamically build some menus and display user specific data and so the application threw exceptions if the session information was NULL. So an attacker getting hold of just the cookie simply encountered exceptions!
So if you are using Forms Authentication, make sure you read the above links and mitigate the vulnerabilities appropriately.