CSRF Protection & Session Management
IntermediateSpring Security enables CSRF protection by default for stateful apps; stateless REST APIs typically disable it and rely on JWT or API-key schemes instead.
Overview
Cross-Site Request Forgery (CSRF) is an attack where a malicious site tricks an authenticated user's browser into sending a request to your API using their session cookie. Spring Security enables CSRF protection by default for stateful (session-based) applications. For stateless REST APIs secured with JWT Bearer tokens — where the token is in the Authorization header, not a cookie — CSRF is not relevant and should be disabled. Session management controls how Spring Security creates and uses HTTP sessions: stateless APIs should use SessionCreationPolicy.STATELESS to prevent session creation entirely, saving memory and avoiding session fixation vulnerabilities.
Disabling CSRF for stateless REST APIs
CSRF protection is only needed when authentication state is carried in a cookie. A JWT Bearer token in the Authorization header is not automatically sent by browsers on cross-origin requests, so CSRF is irrelevant. Disable CSRF and set session policy to STATELESS for all REST APIs secured with JWT.
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
// Stateless REST API — no session, no CSRF needed
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.csrf(AbstractHttpConfigurer::disable)
// JWT resource server — validates Bearer token
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(Customizer.withDefaults()))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.anyRequest().authenticated())
.build();
}
}CSRF for stateful (session-based) apps
For server-rendered apps or SPAs that authenticate via session cookies, keep CSRF enabled. Spring Security uses the Synchronizer Token Pattern: it stores a CSRF token in the session and requires it in a header or form field on state-changing requests (POST, PUT, DELETE). Modern SPAs use the CookieCsrfTokenRepository with HttpOnly=false so JavaScript can read and send the token in a header.
// Stateful app — keep CSRF enabled with cookie-based token
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.maximumSessions(1) // prevent session duplication
.maxSessionsPreventsLogin(false)) // new login kicks out old session
.csrf(csrf -> csrf
// SPA reads token from cookie, sends in X-XSRF-TOKEN header
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// Exclude public endpoints from CSRF (e.g. login form POST)
.ignoringRequestMatchers("/api/auth/login"))
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/**").permitAll()
.anyRequest().authenticated())
.formLogin(Customizer.withDefaults())
.build();
}
// Frontend (JavaScript): read XSRF-TOKEN cookie, send in header
// fetch('/api/orders', {
// method: 'POST',
// headers: { 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN') },
// body: JSON.stringify(order)
// })Session fixation and concurrent session control
Session fixation attacks set a known session ID before login, then use it after the victim authenticates. Spring Security mitigates this by creating a new session after login (SessionFixationProtectionStrategy). Concurrent session control limits how many simultaneous sessions a user can have — useful for single-device login requirements.
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.sessionManagement(session -> session
// Rotate session ID after authentication (default MIGRATE strategy)
.sessionFixation().migrateSession() // NEW_SESSION or NONE also available
// Single active session per user
.maximumSessions(1)
.maxSessionsPreventsLogin(true) // reject new login if session limit reached
.expiredUrl("/login?expired")) // redirect if session expired/kicked out
.build();
}
// Required for concurrent session control
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher(); // notifies Spring Security of session destroy
}
// application.yml — session timeout
server:
servlet:
session:
timeout: 30m # expire sessions after 30 min of inactivityKey Points to Remember
- 1CSRF is only relevant for cookie-based authentication — disable it for JWT Bearer token REST APIs
- 2SessionCreationPolicy.STATELESS prevents Spring Security from creating or using HTTP sessions — mandatory for JWTs
- 3CookieCsrfTokenRepository.withHttpOnlyFalse() allows JavaScript SPAs to read the CSRF token from a cookie
- 4Session fixation: migrateSession() (default) creates a new session after login, copying attributes — prevents fixation attacks
- 5maximumSessions(1) + maxSessionsPreventsLogin(true) enforces single-device login, rejecting new logins when limit is reached
- 6HttpSessionEventPublisher bean is required for concurrent session control to receive session destruction notifications
Interview Questions
Sign in to ask AriaWhy should CSRF be disabled for a REST API secured with JWT Bearer tokens?
What is a CSRF attack and how does the Synchronizer Token Pattern prevent it?
What is session fixation and how does Spring Security mitigate it?
How would you configure a Spring Security app to allow only one active session per user?
How does CookieCsrfTokenRepository work and why does the cookie need HttpOnly=false for SPAs?
Ask Aria about CSRF Protection & Session Management
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.