views:

725

answers:

4

I'm sure we've all worked on, or are aware of web applications (especially in the enterprise) that have tightly bound themselves to the server session. In these cases, it's possible that the session will become corrupted if more than one browser session is open and using the same server session cookie. We've examined all of the options and found the best way to move forward would be to discourage the use of multiple browser sessions that share a server session cookie.

This is only really a problem when a user executes New Window - Ctrl+N in IE or the equivalent of "duplicate tab" in other browsers. Essentially we end up with two active browser sessions sharing the same cookies.

So, to discourage this (as it will likely be inadvertent) I've set out to put some kind of warning system in place to prevent this behavior. Now, our code does plenty of concurrency checking to ensure data integrity, but there can still be issues with data corruption.

My solution, after finding that the general answer is "it's impossible" was to rely on AJAX to send out "pings" and measure the time between. So, we have a general rule: we "ping" at a certain interval and if the delta between the last ping in the current ping is less than the ping duration, we know we have multiple active browser sessions on a single server session.

So, where Pf is ping frequency; Pc is current ping; and Pl is last ping, then we have an error when Pf > (Pc - Pl).

           p1    p2    p3    p4
TAB1 0-----|-----|-----|-----|---...
                 :     :     :
                 :  p1 :  p2 :  p3    p4
TAB2          0-----|-----|-----|-----|---...
     ^     ^     ^  ^  ^  ^  ^  ^
                  Deltas
----+---+------------
TAB | P |   Delta (Pc - Pl)
----+---+------------                 
 1  | 1 |   5
 1  | 2 |   5
 2  | 1 |   2.5 -Error
 1  | 3 |   2.5 -Error
 2  | 2 |   2.5 -Error

Now, if there is network congestion or other factors, then the delta will be greater than the frequency, ruling out false-positives.

We do have a problem if two tabs are open at the exact same momemnt. But, since the ping frequency is just the frequency at which the requests are made, and not a guaranteed elapsed time, we can assume that soon the two browser sessions will begin sliding out of sync.

In the example, I have the ping frequency set to every 5 seconds. If there are 100 simultaneous users then we're lookiing at ~20 requests/second for the ping Servlet/HttpModule. To minimize unnecessary network traffic I was thinking that the ping frequency would decay as time went on until a maximum of 20 pings/second was reached. This would amount to ~5 requests/second with 100 concurrent users. This is a trade-off, though, as it will cause a delay in detection. However, once detection occurs, the frequency resets to 5 pings/second until resolved. (These numbers are just as an example; they would vary a based on the environment)

To minimize concurrency and scalability issues, the last ping timestamp for the session should be kept in the session itself. This will allow any distributed session technology to maintain the availability of the session across JVMs or app domains without our ping service needing to be aware of it.

I'm trying to determine whether or not this is a sound approach of if I'm in for a world of hurt. Any experience with the issue would be helpful.

EDIT: I know this sounds like a band-aid, but this is meant to be a stopgap measure until we can rip out the offending library.

+1  A: 

My major concern would be whether this gets compromised when there's network latency. That is, any trouble would probably come from issues for which ping is generally used for in the first place.

Jon
Latency isn't a huge problem - this is an intranet application; typically gig-e. If there is latency, then this approach fails safely in that no messages would display (if theres a single instance) because the delta would always be *higher* as latency increases.
Ryan Emerle
+1  A: 

If you really are in a bind and cannot fix the application to sanely handle multiple browser instances sharing the same session, then yes, this is a sound approach.

For what it is worth, I have used the exact same concept to enforce concurrent usage license constraints - so it WILL detect multiple users sharing the same "key", which in your case is the session.

I would modify your approach only slightly, and have the ping message contain the date/time of the client - so that you can avoid dealing with network latency at all in your calculations.

Bryan Batchelder
+2  A: 

I worked on a single window web application many years ago (pre-dating "Web 2.0"). We simply launched a new window without any toolbars (no back button, etc) and disabled right-clicking. We took care to create a very usable in-session navigation system. This was enough to prevent almost all accidental duplicate browsing. This was an intranet application; obviously I'd never recommend doing anything like this on a general website.

Personally, I don't like the sound of the ping detector. I would just make sure that no data corruption can ever possibly occur. Multiple browser sessions is not a valid excuse for that... although I understand how it can be problematic. But if you want to add a ping detector on top of perfectly working code, then it might serve as a helpful reminder to the user.

You could add a unique token to the end of every link. If the unique token is used more than once (e.g., opening a new window, bookmarking, back, forward), then the request could be denied. With proper tracking, you could ensure that it's never possible to get from one page to another without taking a valid path. This approach is more reliable than the ping (as it is controlled by the server), but could result in a very annoying user experience.

The short of it is: fix your application to not corrupt any data. I know that may not be a trivial thing, and I don't mean to make light of it. Implementing pings and such may help make the problem disappear, but I guarantee you that if something can go wrong, it eventually will. :-)

konforce
Adding the token isn't really an option. Try hitting CTRL+N in IE.. you get a complete clone of the page, including the URL. Even with a pop-out window (which is not really an option) the CTRL+N nonsense can be problematic.The data corruption is caused by an interface library that we use and thus cannot be easily/practically remedied.I do agree that it sounds like a band-aid, but I'm just looking for *something* to reduce support costs and bug reports.
Ryan Emerle
On every page click, you would delete the previous tokens. Thus opening a new window and clicking on a link would then invalidate all of the links on the first window. It would break many things a user is used to doing (e.g., clicking 'back'), but would guarantee a single, sequential path. If data corruption is critical and your hands are tied regarding the library, then you may have to implement such a draconian system. If data corruption is only a nuisance, then I'd definitely just implement a simpler system (such as your ping idea).
konforce
A: 

A very simple solution that you may or may not be able to use is have a single page (ie, a single URL such as home.whatever) for the ajax application. If they request that same page a second time and there is already a valid session, then you know they opened a new tab or a new window. Note that you wouldn't be able to distinguish this from a refresh, however.

Your solution is way too complicated.

GreenieMeanie
Unfortunately, this is band-aid on an existing application. If I had to radically redesign it, then I would remove the offending components that require this work-around.My solution may seem complicated because it's hard to express in words. The actual implementation would be maybe 10 lines in a Servlet/HttpModule with 10 lines of AJAX. I think the fundamental principle is a simple one.
Ryan Emerle
I am just saying that I have used the solution above successfully before. Some users didn't like how their session expired when they refreshed, but usually they didn't need to since the whole application was ajax and the URL as displayed in their browser never changed.
GreenieMeanie