Session fixation, concurrency and remember-me
The session controls that matter, and the cookie flags that do the real work.
Open this lesson in the learning hubKey points
- Session fixation: an attacker sets a known session id on the victim before login. Unless the id is rotated at authentication, they now share the authenticated session.
- Spring Security rotates by default with
changeSessionId(). Disabling it, or building a custom login that does not rotate, reintroduces the vulnerability. - Cookie flags are the highest-value, lowest-effort control.
HttpOnlyblocks JavaScript access,Secureforbids plaintext transmission, andSameSite=Laxremoves most CSRF exposure at the browser. - Concurrent session control limits how many live sessions one account may have. It requires session state the server can see, so in a clustered deployment it means Spring Session backed by Redis rather than in-memory.
- remember-me is a long-lived credential in a cookie. The persistent-token variant, which rotates the token on each use and detects reuse of an old one, is far safer than the hash-based one.
- Logout must invalidate server-side. Clearing the cookie alone leaves the session valid, so anyone holding the old id - from a log, a proxy or a shared machine - remains signed in.
Example
http
.sessionManagement(s -> s
// Rotate the id at login. This is the fixation defence.
.sessionFixation(f -> f.changeSessionId())
// One live session per account; the older one is expired.
.maximumSessions(1)
.maxSessionsPreventsLogin(false)
.expiredUrl("/login?expired"))
.logout(l -> l
.invalidateHttpSession(true) // server-side, not just the cookie
.deleteCookies("JSESSIONID")
.clearAuthentication(true))
// Persistent tokens: rotated per use, and reuse of an old token is
// treated as theft and invalidates the series.
.rememberMe(r -> r
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(1209600) // 14 days
.useSecureCookie(true));
---
# The cookie flags do most of the work, and cost nothing.
server:
servlet:
session:
timeout: 30m
cookie:
http-only: true # JavaScript cannot read it -> XSS cannot steal it
secure: true # never sent over plain HTTP
same-site: lax # not sent on cross-site POSTs -> most CSRF gone
# Clustered? maximumSessions needs shared session state:
spring:
session:
store-type: redis # in-memory counts only THIS instance sessions
# Attack, and what stops it:
# fixation -> changeSessionId() at login
# XSS theft -> HttpOnly
# network sniff -> Secure + HSTS
# CSRF -> SameSite=Lax, plus CSRF tokens
# shared machine -> server-side invalidation on logout
Rotate the session id at login and set HttpOnly, Secure and SameSite - those four together remove most session attacks.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.