Control the Session with Spring Security

1. Overview

In this article, we’re going to illustrate how Spring Security allows us to control our HTTP Sessions.

This control ranges from a session timeout to enabling concurrent sessions and other advanced security configs.

2. When Is the Session Created?

We can control exactly when our session gets created and how Spring Security will interact with it:

  • always – a session will always be created if one doesn’t already exist
  • ifRequired – a session will be created only if required (default)
  • never – the framework will never create a session itself but it will use one if it already exists
  • stateless – no session will be created or used by Spring Security
<http create-session="ifRequired">...</http>

Java configuration:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
}

It’s very important to understand that this configuration only controls what Spring Security does – not the entire application. Spring Security may not create the session if we instruct it not to, but our app may!

By default, Spring Security will create a session when it needs one – this is “ifRequired“.

For a more stateless application, the “never” option will ensure that Spring Security itself will not create any session; however, if the application creates one, then Spring Security will make use of it.

Finally, the strictest session creation option – “stateless” – is a guarantee that the application will not create any session at all.

This was introduced in Spring 3.1 and will effectively skip parts of the Spring Security filter chain – mainly the session related parts such as HttpSessionSecurityContextRepositorySessionManagementFilterRequestCacheFilter.

These more strict control mechanisms have the direct implication that cookies are not used and so each and every request needs to be re-authenticated. This stateless architecture plays well with REST APIs and their Statelessness constraint. They also work well with authentication mechanisms such as Basic and Digest Authentication.

3. Under the Hood

Before executing the Authentication process, Spring Security will run a filter responsible with storing the Security Context between requests – the SecurityContextPersistenceFilter. The context will be stored according to a strategy – HttpSessionSecurityContextRepository by default – which uses the HTTP Session as storage.

For the strict create-session=”stateless” attribute, this strategy will be replaced with another – NullSecurityContextRepository – and no session will be created or used to keep the context.

4. Concurrent Session Control

When a user that is already authenticated tries to authenticate again, the application can deal with that event in one of a few ways. It can either invalidate the active session of the user and authenticate the user again with a new session, or allow both sessions to exist concurrently.

The first step in enabling the concurrent session-control support is to add the following listener in the web.xml:

<listener>
    <listener-class>
      org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

Or define it as a Bean – as follows:

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
    return new HttpSessionEventPublisher();
}

This is essential to make sure that the Spring Security session registry is notified when the session is destroyed.

To enable the scenario which allows multiple concurrent sessions for the same user the <session-management> element should be used in the XML configuration:

<http ...>
    <session-management>
        <concurrency-control max-sessions="2" />
    </session-management>
</http>

Or, via Java configuration:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement().maximumSessions(2)
}

5. Session Timeout

5.1. Handling the Session Timeout

After the session has timed out, if the user sends a request with an expired session id, they will be redirected to a URL configurable via the namespace:

<session-management>
    <concurrency-control expired-url="/sessionExpired.html" ... />
</session-management>

Similarly, if the user sends a request with a session id which is not expired, but entirely invalid, they will also be redirected to a configurable URL:

<session-management invalid-session-url="/invalidSession.html">
    ...
</session-management>

The corresponding Java configuration:

http.sessionManagement()
  .expiredUrl("/sessionExpired.html")
  .invalidSessionUrl("/invalidSession.html");

5.2. Configure the Session Timeout with Spring Boot

We can easily configure the Session timeout value of the embedded server using properties:

server.servlet.session.timeout=15m

If we don’t specify the duration unit, Spring will assume it’s seconds.

In a nutshell, with this configuration, after 15 minutes of inactivity, the session will expire. The session after this period of time is considered invalid.

If we configured our project to use Tomcat, we have to keep in mind that it only supports minute precision for session timeout, with a minimum of one minute. This means that if we specify a timeout value of 170s for example, it will result in a 2 minutes timeout.

Finally, it’s important to mention that even though Spring Session supports a similar property for this purpose (spring.session.timeout), if that’s not specified then the autoconfiguration will fallback to the value of the property we first mentioned.

6. Prevent Using URL Parameters for Session Tracking

Exposing session information in the URL is a growing security risk (from place 7 in 2007 to place 2 in 2013 on the OWASP Top 10 List).

Starting with Spring 3.0, the URL rewriting logic that would append the jsessionid to the URL can now be disabled by setting the disable-url-rewriting=”true” in the <http> namespace.

Alternatively, starting with Servlet 3.0, the session tracking mechanism can also be configured in the web.xml:

<session-config>
     <tracking-mode>COOKIE</tracking-mode>
</session-config>

And programmatically:

servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));

This chooses where to store the JSESSIONID – in the cookie or in a URL parameter.

7. Session Fixation Protection With Spring Security

The framework offers protection against typical Session Fixation attacks by configuring what happens to an existing session when the user tries to authenticate again:

<session-management session-fixation-protection="migrateSession"> ...

The corresponding Java configuration:

http.sessionManagement()
  .sessionFixation().migrateSession()

By default, Spring Security has this protection enabled (“migrateSession“) – on authentication a new HTTP Session is created, the old one is invalidated and the attributes from the old session are copied over.

If this is not the desired behavior, two other options are available:

  • when “none” is set, the original session will not be invalidated
  • when “newSession” is set, a clean session will be created without any of the attributes from the old session being copied over

8. Secure Session Cookie

Next, we’ll discuss how to secure our session cookie.

We can use the httpOnly and secure flags to secure our session cookie:

  • httpOnly: if true then browser script won’t be able to access the cookie
  • secure: if true then the cookie will be sent only over HTTPS connection

We can set those flags for our session cookie in the web.xml:

<session-config>
    <session-timeout>1</session-timeout>
    <cookie-config>
        <http-only>true</http-only>
        <secure>true</secure>
    </cookie-config>
</session-config>

This configuration option is available since Java servlet 3. By default, http-only is true and secure is false.

Let’s also have a look at the corresponding Java configuration:

public class MainWebAppInitializer implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext sc) throws ServletException {
        // ...
        sc.getSessionCookieConfig().setHttpOnly(true);        
        sc.getSessionCookieConfig().setSecure(true);        
    }
}

If we’re using Spring Boot, we can set these flags in our application.properties:

server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true

Finally, we can also achieve this manually by using a Filter:

public class SessionFilter implements Filter {
    @Override
    public void doFilter(
      ServletRequest request, ServletResponse response, FilterChain chain)
      throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        Cookie[] allCookies = req.getCookies();
        if (allCookies != null) {
            Cookie session = 
              Arrays.stream(allCookies).filter(x -> x.getName().equals("JSESSIONID"))
                    .findFirst().orElse(null);

            if (session != null) {
                session.setHttpOnly(true);
                session.setSecure(true);
                res.addCookie(session);
            }
        }
        chain.doFilter(req, res);
    }
}

9. Working With the Session

9.1. Session Scoped Beans

A bean can be defined with session scope simply by using the @Scope annotation on beans declared in the web-Context:

@Component
@Scope("session")
public class Foo { .. }

Or with XML:

<bean id="foo" scope="session"/>

Then, the bean can simply be injected into another bean:

@Autowired
private Foo theFoo;

And Spring will bind the new bean to the lifecycle of the HTTP Session.

9.2. Injecting the Raw Session into a Controller

The raw HTTP Session can also be injected directly into a Controller method:

@RequestMapping(..)
public void fooMethod(HttpSession session) {
    session.setAttribute(Constants.FOO, new Foo());
    //...
    Foo foo = (Foo) session.getAttribute(Constants.FOO);
}

9.3. Obtaining the Raw Session

The current HTTP Session can also be obtained programmatically via the raw Servlet API:

ServletRequestAttributes attr = (ServletRequestAttributes) 
    RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(true); // true == allow create

10. Conclusion

In this article, we discussed managing Sessions with Spring Security. Also, the Spring Reference contains a very good FAQ on Session Management.

As always, the code presented in this article is available over on Github. This is a Maven-based project, so it should be easy to import and run as it is.