Looking for someone to either confirm or refute my theory that deploying two iframes pointing to two different stateful pages on the same domain can lead to JSESSIONIDs being overwritten. Here's what I mean:
Setup
- suppose you have two pages that require HttpSession state (session affinity) to function correctly - deployed at http://www.foo.com/page1 and http://www.foo.com/page2
- assume www.foo.com is a single host running a Tomcat (6.0.20, fwiw) that uses JSESSIONID for session id's.
- suppose these pages are turned into two iframe widgets to be embedded on 3rd party sites: http://www.site.com/page1" /> (and /page2 respectively)
- suppose there a 3rd party site that wishes to place both widgets on the same page at http://www.bar.com/foowidgets.html
Can the following race condition occur?
- a new visitor goes to http://www.bar.com/foowidgets.html
- browser starts loading URLs in foowidgets.html including the two iframe 'src' URLs
- because browsers open multiple concurrent connections against the same host (afaik up to 6 in chrome/ff case) the browser happens to simultaneously issue requests for http://www.foo.com/page1 and http://www.foo.com/page2
- The tomcat @ foo.com receives both requests at about the same time, calls getSession() for the first time (on two different threads) and lazily creates two HttpSessions and, thus, two JSESSIONIDs, with values $Page1 and $Page2. The requests also stuff data into respective sessions (that data will be required to process subsequent requests)
- assume that the browser first receives response to the page1 request. Browser sets cookie JSESSIONID=$Page1 for HOST www.foo.com
- next response to the page2 request is received and the browser overwrites cookie JSESSIONID for HOST www.foo.com with $Page2
- user clicks on something in 'page1' iframe on foowidgets.html; browser issues 2nd request to http://www.foo.com/page1?action=doSomethingStateful. That request carries JSESSIONID=$Page2 (and not $Page1 - because cookie value was overwritten)
- when foo.com receives this request it looks up the wrong HttpSession instance (because JSESSIONID key is $Page2 and NOT $Page1). Foobar!
Can the above happen? I think so, but would appreciate a confirmation.
If the above is clearly possible, what are some solutions given that we'd like to support multiple iframes per page? We don't have a firm need for the iframes to share the same HttpSession, though that would be nice. In the event that the solution will still stipulate a separate HttpSession per iframe, it is - of course - mandatory that iframe 1 does not end up referencing httpSession state for iframe 2 instead of own.
off top of my head I can think of:
- map page1 and page2 to different domains (ops overhead)
- use URL rewriting and never cookies (messes up analytics)
- anything else?
thanks a lot, -nikita
What you are saying is correct, that's the raison d'etre of the method HttpServletResponse.encodeURL().
If the page containing the two iframes is in the same context as page1 and page2, the URLs in the iframes should be encoded with this method or obtained with JSTL's <c:url> tag.
It will add the JSESSIONID in the URL if there is no cookie defined yet.
If page1 and page2 use different context, they will both work through third party iframes without interfering each others scope.
There are various ways to control sessions in JSP. This question's top answer might help you figure out a proper solution: Under what conditions is a JSESSIONID created?
TL;DR The scenario is correct and one session overrides the other and both pages share the session; but it doesn't matter.
In the example above, you have two near-simultaneous stateless anonymous requests.
In other words, there is absolutely nothing unique about the request; two generic pages will be return. Both these pages would have new JSESSIONIDs not because of the race, but because the requests themselves are anonymous and therefore essentially ask Tomcat to create new sessions.
Lets assume that page2 won the JSESSIONID speed contest and the browser now has the page2 cookie. Then the user clicks on an action in page1. I think you're correct that the request will come labelled with the page2 cookie.
But so what?
Page1 cannot have any session-related information in it, and therefore no user-specific information. The actions from it can therefore have no session-related state (the state was just created). If there is no specific session-related state, then there is no issue with it coming with the 'wrong' JSESSIONID.
Looking at it in another way: If the request for page2 had been completely processed before the request for page1, in what way would page1 be different? I can't see any differences. If there are no differences in the returned HTML in the two scenarios, then it doesn't matter its JSESSIONID is swapped.
OTOH, if the user has already visited bar.com, then the requests for both page1 and page2 will be associated with the same JSESSIONID, the pages that are returned are correct and all is good in the world of foo.com.
One issue: If you have CSRF protection switched on. CSRF libraries modify all URLs in the returned page to include an extra parameter. The CSRF protection library checks all incoming requests that their security token matches the JSESSIONID. If page1 uses the cookie for page2, the CSRF protection would reject the request as forged.
If you have to have one session per iframe: Use URL rewriting. This was originally designed for managing sessions when the browser doesn't accept cookies. It works well but the URLs look nasty.