0

I've inherited an old ASP.Net Webforms application that makes heavy use of Session variables to store the database record IDs of user-submitted applications. This has caused some severe issues where users open up forms for different applications in multiple tabs, unwittingly overwriting information in one app with data meant for another.

Common usage throughout the application looks something like this:

// Get the application ID from the database
var appID = Convert.ToInt32(Session["appID"]);

// Update application using the above ID
UpdateDB("UPDATE Application SET Title='MY TITLE' WHERE id=" + appID);

// Redirect to another step of the form
Response.Redirect("/application/step-2");

Since this issue persists across numerous pages of the application, the solutions I've found are less than ideal (detailed at the end of this post).

My question: is there any way for me to prevent new tabs from overwriting existing session variables without having to rewrite Session access across the entire application?

Here are the solutions I've found that are more a less a last resort due to how much of the application would need to be changed:

  • Prepend ViewState("_PageID") to the session variable

  • Use cookieless sessions (stored in the URL)

*The above solutions were found on the related post: asp.net - session - multiple browser tabs - different sessions?

Community
  • 1
  • 1
MyNameIsKo
  • 2,171
  • 1
  • 15
  • 24
  • Why are you using Session if the things you're storing in there are not related to the user's session? – mason Feb 01 '17 at 21:43
  • @mason, while I can't speak for the original programmer, my best guess is that they wanted a way to store the app ID across multiple pages of the form. Of course, the method they chose is far from ideal, with the most severe consequence being the issue I brought up in my question. – MyNameIsKo Feb 01 '17 at 21:47
  • 1
    My point is that *do not use Session* if you don't intend to use it for things that aren't related to a user's session. Use a query string parameter, or view state etc. Decide what's appropriate based on the scope the variable should have. – mason Feb 01 '17 at 21:50
  • @mason, I'm aware that Session is being misused here but rewriting the many portions of the project that use Session in this way is not currently feasible with the time I have. – MyNameIsKo Feb 01 '17 at 21:58
  • @mason says there is no need to save app id in session that will repeat for every user. You can do: var appID = Convert.ToInt32(Application["appID"]); – Farzin Kanzi Feb 01 '17 at 23:20

2 Answers2

1

It sounds like AppID is not logically a property of your session. It is a property of the page, or possibly of a workflow.

If you need to store a property of a page that persists between postbacks, you can store it like this:

this.ViewState["AppID"] = appID;

And to perform the update

var appID = (int)ViewState["appID"]
UpdateDB("UPDATE Application SET Title='MY TITLE' WHERE id=" + appID);

Be sure to secure your ViewState or else malicious users could tamper with the AppID and update someone else's record.

ViewState is stored in the page itself, so will not leak between tabs.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • I don't think ViewState works in this situation since the form is spread across multiple pages, each one linking to the other with a Response.Redirect. The AppID will get lost. – MyNameIsKo Feb 02 '17 at 17:05
1

Sounds like it is not feasible to rewrite the code to store data somewhere other than session.

If that's truly the case, perhaps your easiest option is to add code to your site to display an error whenever the user opens up the site in a second tab, or if activity in a different tab modifies the AppID.

Prevent multiple tabs completely

Here is one way to do it, high level:

  1. Add code to Application_EndRequest to output a "microsession" cookie containing a timestamp or random nonce with each and every page. Note: You must be careful NOT to output this cookie for resource requests, e.g. images or CSS. Only output if the current request is for a page.

  2. Add a snip of javascript to your common.js file (or whatever location will cause it to run for every page in your site) which does the following:

    a. On page load, grab the microsession cookie and store its value in a Javascript variable

    b. Occasionally check the microsession cookie to see if it has changed. You can do this with a timer, or you can just check when the user is submitting any form. Be aware that some devices will pause scripts when the tab loses focus, so if you are using a timer, you should also check when the tab gets focus again.

    c. If the cookie has changed, that means that the user requested a page from your site but in a different tab. Display an error. At that point you can either force them to log off or just Session.Abandon() and redirect them to the home page, where a new, clean session will automatically be constructed for them.

Allow multiple tabs as long as AppID doesn't change

If you want to allow multiple tabs and just want to cause an error when there is an unexpected change in AppID, you can set the microsession cookie to a hash of the AppID instead of the nonce. That way the error will only trigger if the AppID changes. If there is more than one session variable you need to check, you can concatenate them (string concatenation is fine) before generating the hash.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • This sounds brilliant. I'll give it a shot next week and will mark this as the answer if it works and there's no alternative, simpler solution. – MyNameIsKo Feb 03 '17 at 16:07