Scenario
- I restart the server and browser so there's no session data.
- I go to www.someurl.com public access page. My controller gets me a session with this
HttpSession session=request.getSession(true);
- I click on a plane anchor link to www.someurl.com/admin restricted access page which opens in a new tab. Spring Security 3 intercepts this and challenges for credentials. I log in successfully.
- I go back to the previous tab with www.someurl.com and refresh the page.
Problem
What I notice in my controller for www.someurl.com is that the session id is different on step 2 and step 4. Looks like Spring Security created a new session and that session is now attached to the request for public page. Why does this happen and can I force Spring Security to use existing session?
Traced scenario
- Restart browser and server so no session data exist(s).
- I go to www.someurl.com. Controller has request injected. request.session is null. getSession(true) gets me a session with id 87B091B12F38D44C53AF0DA9E2147484. LogService gets the request object and also does getSession(true) but gets session with id 87B091B12F38D44C53AF0DA9E2147484 so all's good thus far.
- I click /admin. Page opens up in new tab. I log in.
- I refresh the www.someurl.com. Controller has request injected. request.session is not null. Session id is 547DF59035C91783D783BAEF4A15FBFF.
You got your diagnostic wrong:
What I notice in my controller for www.someurl.com is that the session id is different on step 2 and step 4. Looks like Spring Security created a new session and that session is now attached to the request for public page.
It's precisely because all the pages use the same session that when you go back to the first tab and refresh, you're still logged in as an admin. All the tabs and frames of a given browser share the same session for a given webapp. That's how it works. The server doesn't know and care about browser tabs. It gets a session cookie attached to all the requests sent by a given browser, and uses this cookie to get the corresponding session. This is actually a good thing. Without that, each time you open a new tab once already authenticated, you would have to authenticate again. And you definitely don't want that.
So let's explain what happens in your scenario:
- you restart the server and browser so there's no session data.
- you go to www.someurl.com public access page. Your controller gets you a session. A cookie is sent back to the browser
- You click on a plane anchor link to www.someurl.com/admin restricted access page which opens in a new tab. The cookie is sent with the request, so this request is part of the session opened at step 2. Spring Security 3 intercepts this and challenges for credentials. It attaches credentials to the session, which is now an authenticated session
- You go back to the previous tab with www.someurl.com and refresh the page. The cookie is sent again, and Spring knows that you're the guy who authenticated at step 3, because the authentication credentials are stored in session.
EDIT: it appears I was wrong, and Spring indeed creates a new session after login to prevent session fixation attacks. Explanations about why this is useful, and how to avoid this behavior are available in the documentation:
Session fixation attacks are a potential risk where it is possible for a malicious attacker to create a session by accessing a site, then persuade another user to log in with the same session (by sending them a link containing the session identifier as a parameter, for example). Spring Security protects against this automatically by creating a new session when a user logs in. If you don't require this protection, or it conflicts with some other requirement, you can control the behaviour using the session-fixation-protection attribute on , which has three options
migrateSession - creates a new session and copies the existing session attributes to the new session. This is the default.
none - Don't do anything. The original session will be retained.
newSession - Create a new "clean" session, without copying the existing session data.
You need to change the behavior of HttpSessionRequestCache
so that it doesn't create a session if one does not exist.
You can do it by creating its instance in your XML configuration as below:
<beans:bean id="httpSessionRequestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
<beans:property name="createSessionAllowed" value="false" />
</beans:bean>
Then you need to configure the http element in your spring security config to use your bean instance:
<http auto-config="true" ....>
<request-cache ref="httpSessionRequestCache"/>
... rest of your config
</http>
If you're using JSP, you should also prevent the JSP from creating a session. In order to do it, you need to add a page
directive on top of every JSP (even the ones that are included from other JSP's).
<%@ page session="false" %>