Asp.net Sessions Getting Crossed / Mixed Up

2019-01-13 11:42发布

问题:

Few weeks ago we had one of our customers contacting us saying that sometimes when he creates an activity it gets created under someone else's name!

We did some troubleshooting and couldn't find anything. We asked the user to contact us the next time he was experiencing these issues. He did contact us and we were able to do a gotomeeting with him and see the issue with our own eyes.

It was not only the activities, he was recognized as someone else in the application. He had access to everything that other person should had access to. That was when we realized we are having a session mixed up issue.

A little bit about our code:
Like any other application we have a simple login page that user enter email and password and we authenticate them against our database and if they are valid we call FormsAuthentication.SetAuthCookie() to save current user id in the cookie and we let him in.

BL.User currentUser = BL.User.Authenticate(txtUsername.Text, txtPassword.Text);

if (currentUser != null)
{
    this.Session["NumberOfLoginTried"] = "0";
    FormsAuthentication.SetAuthCookie(currentUser.UserID.ToString(), chRememberMe.Checked);
    Response.Redirect(FormsAuthentication.GetRedirectUrl(currentUser.UserID.ToString(), false));
}

We also use the following piece of code to get logged-in user id (current user) in our application.

public static int GetCurrentUserID()
{
    int userID = -1;
    int.TryParse(HttpContext.Current.User.Identity.Name, out userID);
    return userID;
}

And yes we did our homework and googled around and have seen the following two links:

http://lionsden.co.il/codeden/?p=446
ASP.NET Session Mix-up using StateServer (SCARY!)

We have disabled kernel-mode caching and user-mode caching for .aspx and .ascx files and this is still happening.

P.S- The app is running on Windows 2008 R2 with IIS 7.5. And we are NOT using cookieless session.

回答1:

We have just had a very similar problem, which occured at random, seemingly un-reproducibly.

The problem turned out to be ASP.NETs Page caching mechanism - in our case the <%@ OutputCache tag in particular.

There was a line we had used <%@ OutputCache NoStore="true" Duration="1" %> that basically meant if two users accessed the same page within 1 second of each other they would see the same page (including the logged in username of the other user). So if they refreshed said page, they got the correct information.

In our case, changing said line to <%@ OutputCache NoStore="true" Duration="1" VaryByParam="*" %>, disabling kernel caching in IIS as in this link (http://lionsden.co.il/codeden/?p=446)

and adding the following lines to the Page_Load event of the page in question:

Response.CacheControl = "private";
Response.ExpiresAbsolute = DateTime.Now.AddDays(-1d);
Response.Cache.SetCacheability(HttpCacheability.NoCache);

Seems to have solved the problem for us. Hopefully this helps someone else with a similar issue.



回答2:

We had the same problem and it was caused by the <clientCache/> setting in IIS, which by default fails to add the Cache-Control: private HTTP header. The lack of this header meant that our Forms Authentication cookies were being cached by downstream proxy servers! So when our site got busy, all of a sudden a load of users would suddenly get logged in as the wrong user! Nightmare.



回答3:

if removing the <%@ OutputCache NoStore="true" Duration="1" VaryByParam="*" at all (in all ascx files being in the line from Master to aspx too !!!) prevented from cross-sessions. having only one ascx with outputcache directive loaded, cross-sessions occured.

It did not matter in my case if using sessionstat InProc ore StateServer, if having cookieless or cookie sessions.



回答4:

We had the same problem at the company I work at. We also realized that it was caused by output caching, which resulted in sending someone else's SessionId to the wrong person.

We have now added the following <caching> element to our web.config.

<configuration>
  [...]
  <system.webServer>
    [...]
    <caching enabled="false" enableKernelCache="false">
    </caching>
  </system.webServer>
  [..]
</configuration>

We can't guarantee this will solve it, because the problem is almost impossible to reproduce, but based on our research, this should solve it.

Strangely, the link to a Microsoft article describing this problem that can be found on the internet gives some general page as if the page has been deleted.

But there is this Microsoft article that seems to describe the same issue voor IIS 6:

An ASP.NET page is stored in the HTTP.sys kernel cache in IIS 6.0 when the ASP.NET page generates an HTTP header that contains a Set-Cookie response

Which describes the symptom as:

Consider the following scenario. A Microsoft ASP.NET page contains the <%@ OutputCache %> directive. Additionally, the ASP.NET page generates an HTTP header that contains a Set-Cookie response. In this scenario, the ASP.NET page is stored in the HTTP protocol stack (HTTP.sys) kernel cache in Microsoft Internet Information Services (IIS) 6.0. Therefore, multiple users who access the same page may receive identical cookies.

Update

I found this really good article at Microsoft Premier Developer blog that explains a lot:

ASP.Net Session Swapping – Why it happens and what can be done about it?



回答5:

Because you all disabled kernel-mode caching, I like to point out some other thinks.

1) To correctly use the HttpContext.Current.User.Identity.Name, you first need to verify that your user is logedin by using the User.Identity.IsAuthenticated

2) in this point Session.Add("CurrentUser", currentUser); what are you actual try to save ?

Now I think that is the problem is on cache. The pages are stored somewhere in between your users and the one mix up with the other. Some of the headers that you can use to your page to avoid the cache on the middle proxy computers.

Response.Cache.SetExpires(DateTime.UtcNow.AddYears(-2));
Response.Cache.SetNoStore();
Response.Cache.SetValidUntilExpires(false);
Response.Cache.SetCacheability(HttpCacheability.NoCache);                
Response.ExpiresAbsolute = DateTime.Now.Subtract(new TimeSpan(1, 0, 0, 0));
Response.Expires = 0;
Response.CacheControl = "no-cache";
Response.AppendHeader("Pragma", "no-cache");

Also I say that if your pages have data that you do not wish to share among your user you need to use Secure HTTPS pages, and set your cookies to be available only on secure pages by adding <httpCookies httpOnlyCookies="true" requireSSL="true" /> on web.config

Also, check if you save your session on SQL server that you scheduled run the clean up routing every 1 minute.

To been able to find some more information I suggest to store some hidden text on the pages, eg the date-time of the rendered, maybe a the last 4 digit of the userID, and what else you may thing that can help you see if the page come from a cache or not.



回答6:

Since this seems to fall into the extremely arcane problem territory, maybe it's time for a leap.

You could stop using the ASP.NET session to store your identifiers altogether.

You have a bunch of options of where you could stick this information instead. You could choose to encrypt it into the Forms Authentication ticket's UserData property (I've done this before in production, it works great for storing a key(s), csv of roles, or even small json objects). Past the forms auth ticket, you could write the information directly as your own cookie. You could also bypass cookies altogether.

If you choose to bypass the cookies, you're basically entering into similar territory of the cookieless ASP.NET sessions. You have a couple of options, you could make the user identifier be apart of every single url as a query parameter. Another option would be to create a HttpModule that would add a hidden form input into every page response that contains the logged in user's identifier.

If you go down the cookieless path absolutely make sure it's not possible to use your site as HTTP and every single request is HTTPS. Even more especially if you use the query parameter method.



回答7:

If you checked that output caching is not the problem

There is already on answer from me here, but as it turned out my other solution (disabling the output cache) did not really solve our problem for us.

Since in the question it is stated that caching is turned off, the only other possible (AFAIK) bug that can produce this is what turned out to be the real culprit in our case: we use a private variable in a ActionFilterAttribute.

And because these are cached, storing private/personal data from a user in this way can also lead to session mix-up!

This answer describes what our problem was and how to fix it:

https://stackoverflow.com/a/8937793/1864395

Also, I think it's good to mention that we were able to reproduce the problem by running Apache JMeter with a scenario for several users at the same time. It's a really nice tool (although not really user friendly/intuitive) used for (among other things) stress-testing. It's probably the only way to diagnose session mix-ups!