The OWASP Session Management best practices recommend using an absolute session timeout in addition to an idle timeout.
Spring Session currently supports only inactivity timeouts. It would be nice for the library to accommodate absolute timeouts as well.
@sirianni Thanks for the report. I don't think that makes sense to have every feature in Spring Session itself. There is already a Session.getCreationTime() that allows you to determine how long the session has been around. With this information, you can calculate an absolute timeout of our choice. If the timeout has occurred, you can invalidate the session.
Thanks @rwinch . Do you have any initial quick thoughts on where the best place would be to wedge in this behavior?
You could provide a delegating implementation of the SessionRepository interface that delegates to another implementation of SessionRepository (i.e. Redis) and then when it retrieves the session determines if the creation date is past the absolute session timeout. If it is, then you would invalidate the session and return a null value for the lookup.
@sirianni You can also implement this by providing a DefaultCookieSerializer that has the maxAge property set to the absolute timeout. Keep in mind that this just removes the cookie reference after that amount of time. This means a user interacting with the application through a browser would experience it as though the session has a max expiration because the cookie would be discarded after that max timeout. However, if a malicious user got a hold of the session id, they could continue to use that session past the max expiration.
@rwinch I tried implementing a delegating implementation like you proposed. Currently we are using Redis as a backend for storing the sessions, so we are using @EnableRedisHttpSession. But as far as I understood to provide our own (delegating) SessionRepository we would need to fall back to using @EnableSpringHttpSession and provide a Bean named sessionRepository. But how exactly are we supposed to get an instance of the RedisIndexedSessionRepository we then can delegate to? Of course I could simply create my own instance by using new RedisIndexedSessionRepository() but what about all the stuff around it like the RedisHttpSessionConfiguration.SessionCleanupConfiguration, etc.? So I basically copied the whole RedisHttpSessionConfiguration to create an instance the same way it would have been originally created. But is that really how we are supposed to do this, by copying a whole class? 馃
There are likely a lot of ways of going about this, but the easiest one that comes to mind is using a BeanPostProcessor. Something like:
public class AbsoluteSessionRepositoryBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof SessionRepository) {
return new AbsoluteSessionRepository((SessionRepository) bean);
}
return bean;
}
}
Then you need to expose the BeanPostProcessor on a @Configuration (to avoid circular bean dependencies I recommend doing this in it's own configuration with a static method).
@Configuration
public class AbsoluteSessionRepositoryBeanPostProcessorConfiguration {
@Bean
public static AbsoluteSessionRepositoryBeanPostProcessor AbsoluteSessionRepositoryBeanPostProcessor() {
return new AbsoluteSessionRepositoryBeanPostProcessor();
}
}
It wasn't just as simply as that, but it was definitely a push in right direction for me. I had to return a CGLIB proxy in the BeanPostProcessor otherwise RedisHttpSessionConfiguration$SessionCleanupConfiguration (and maybe others) would give a BeanNotOfRequiredTypeException because the AbsoluteSessionRepository is not of type RedisIndexedSessionRepository
Most helpful comment
@rwinch I tried implementing a delegating implementation like you proposed. Currently we are using Redis as a backend for storing the sessions, so we are using
@EnableRedisHttpSession. But as far as I understood to provide our own (delegating)SessionRepositorywe would need to fall back to using@EnableSpringHttpSessionand provide a Bean namedsessionRepository. But how exactly are we supposed to get an instance of theRedisIndexedSessionRepositorywe then can delegate to? Of course I could simply create my own instance by usingnew RedisIndexedSessionRepository()but what about all the stuff around it like theRedisHttpSessionConfiguration.SessionCleanupConfiguration, etc.? So I basically copied the wholeRedisHttpSessionConfigurationto create an instance the same way it would have been originally created. But is that really how we are supposed to do this, by copying a whole class? 馃