Trifork Blog

Axon Framework, DDD, Microservices

Session Timeout and Concurrent Session Control with Spring Security and Spring-MVC

February 28th, 2014 by
|

security-icon

A web application me and my team are building recently underwent a security review. As usual, because you haven’t yet had time to put any real effort into it, some security risks did surface. We use Spring Security and Spring-MVC and I will talk about implementing a session timeout and concurrent session control: nice subjects from the trenches.

In general, sessions should be managed as restrictively as possible for your web application. Category number two on OWASP top ten security threats of 2013 is broken authentication and session management. Here you can find some nice examples of the problem never lying with the internet, but with the human mistakes in using it.

 

Session timeout

An obvious mistake a user can make is forgetting to log out on a public computer, or if he’s unlucky – on a private device that is stolen at a later time. This alone is enough reason to invalidate a user session after a certain time, e.g. fifteen minutes. Another reason is that this also limits possibilities for smart hackers: especially in combination with other attacks like CSRF or click-jacking, session hijacking is a big risk. Moreover, if an account is important enough, physical break-ins to get to a device with a session that remains valid is not unimaginable. For these reasons it is also very important never to expose a session ID in a URL, but I’m getting off topic here …

Setting the timeout is easy, so this really is a quick win. In your web.xml, add the timeout to your session-config:

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

The tracking-mode plays no essential role here, but it is what we use in our app.

Concurrent session control

Concurrent session control for our use case amounts to controlling the number of sessions a user can have at the same time. Normally, each user has its own account, so it’s logical that a user can be logged in only once at a time. Since users might use different devices you might want to allow more than one session, but a maximum for the number of sessions is good practice.

Restricting the number of concurrent sessions will make sure accounts can no longer be shared between multiple or too many users. While not strictly security as I see it, this is also useful if you supply software with paid subscription accounts. For a security critical application, it is likely that if a user tries to log in a second time he or she forgot to log out the first time. In that case, it is a good idea to log out the user from the initial session, which is the approach we took.

We specified the timeout in the web.xml, which is handled by the servlet container (Tomcat in our case) – no Spring Security involved yet. However, to apply application logic to your sessions your application needs knowledge of the user sessions. You need to inject a session events listener in your web.xml:

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

The HttpSessionEventPublisher listens to the servlet container for sessions being created or destroyed and publishes corresponding Spring Security events. This will make sure that when a timeout triggers a session invalidation the application will know about it. We will manage session concurrency in the application, so this is critical. Also note that injecting this listener provides the SessionRegistry, which can now be @Autowired even if you have not defined it explicitly. The SessionRegistry is a nice entry point to get information on the current logged in users, even if you’re not interested in concurrent session control.

Next is injecting a ConcurrentSessionControlAuthenticationStrategy. Using the security xml namespace makes this real easy. In your security configuration, add

<beans:beans xmlns="http://www.springframework.org/schema/security" ... />
<http>
  <session-management>
    <concurrency-control
       max-sessions="1"
       expired-url="/your-page-here" />
  </session-management>
</http>

Note that the element automatically registers a ConcurrentSessionFilter in the filter stack. Set max-sessions="-1" in case you do not want to limit the number of sessions, but still would like a reference to the SessionRegistry as explained above.

Behaviour when Concurrent Sessions are exceeded

There are two possible ways to have your applications handle a login attempt that exceeds the allowed number. The default is that previous sessions are invalidated and if the user tries to use his initial session he will be redirected to “your-page-here”. This is probably best for most applications. The other option is to throw an exception after the login attempt and keep the former session(s) valid. To do that, add the attribute error-if-maximum-exceeded="true" to the <concurrency-control/>.

Warning: implement equals() and hashcode() on your UserDetails

Most applications implement their own user repository and UserDetails implementation. In that case, a warning is in order. Using some of the default Spring Security classes you get the following: ConcurrentSessionControlAuthenticationStrategy calls SessionRegistryImpl.getAllSessions() for the principal, which uses a Map from principal to sessions. The principal is a UserDetails instance, so if you implements UserDetails the equals() and hashcode() should be overridden. Otherwise, the strategy will not work.

Note on multi-node environments

The setup for concurrent session control up to now works perfectly for single node setups. However, if the web application is hosted on multiple servers you need to write your own implementation of SessionRegistry to have a single session registry for all nodes. However, the current implementation of ConcurrentSessionControlAuthenticationStrategy expires the session object directly (which is a simply bean built by the registry to represent the session), instead of delegating this to the registry. So, until this is changed in Spring Security, you would also have to implement and inject your own SessionAuthenticationStrategy.

6 Responses

  1. March 5, 2014 at 21:15 by Arik

    Interesting post Quinten. I had no idea about this (almost complete) built-in support for session management and session concurrency in Spring Security. On a previous job I had we had a similar requirement of being able to logging in just once to the application for security reasons. We didn’t however actually end up implementing it due to time constraint. Had I had this data at the time though, we might have been able to squeeze this feature in… You live and you learn I guess. 🙂

  2. March 6, 2014 at 14:56 by The Baeldung Weekly Review 9

    […] => Session Timeout and Concurrent Session Control with Spring Security and Spring-MVC […]

  3. July 6, 2014 at 15:48 by Eddy

    Hello,thank you for the post. i am having a problem with implementing the hashcode and equals, i am trying to find the proper way of overriding these methods and it seems there is no documentation on it. How is someone supposed to this in order to use the concurrencyfilter?

  4. July 15, 2014 at 15:30 by Quinten Krijger

    Hi Eddy,

    There is a great chapter online on equals() and hashcode(): http://web.archive.org/web/20110622072109/http://java.sun.com/developer/Books/effectivejava/Chapter3.pdf from Jashua Blochs Effective Java. You want to make sure that different instances of UserDetails that actually refer to the same user are equal to each other (i.e. equals() returns true), and not equal if they refer to different users. In principal, it depends on your implementation, but using the username seems a logical choice.

  5. February 10, 2015 at 16:51 by Jasmine

    If I have two concurrent sessions for the same user login, if the original session is timeout, how can I keep the second session still to be valid but invalidate the original session?

  6. February 12, 2015 at 19:42 by yathirigan

    Can i manage the complete HTTPSession lifecycle (Creation, Update, Destroy and Expiry) only with Spring Session and without using Spring Security ?