skip to content

CSRF Protection

CSRF protection issues a token the server checks on state-changing requests, stored in the session or a cookie for SPA use. Interviewers want to hear when disabling it is legitimate — a stateless bearer-token API — and when it is just switching off a warning.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

questions

5

What is CSRF, and how does Spring Security protect against it by default?

level: juniorimportance: must knowfreq 72%

answer

  1. browser auto-sends cookies cross-site
  2. CsrfFilter + secret token on POST/PUT/DELETE/PATCH
  3. GET/HEAD/OPTIONS/TRACE exempt
  4. HttpSessionCsrfTokenRepository default
  5. mismatch -> 403

basics

~10 s

CSRF tricks a logged-in user's browser into sending an unwanted state-changing request using their cookies. Spring Security defends with a secret token (CsrfFilter) that must accompany every POST/PUT/DELETE/PATCH, which an attacker's site cannot know.

solid answer

~40 s

Cross-Site Request Forgery (CSRF) abuses the fact that browsers automatically attach ambient credentials (session cookies) to any request to your domain, so a malicious page can silently trigger a state-changing action as the victim. Spring Security's `CsrfFilter` requires a per-session/per-request secret token on unsafe methods (POST, PUT, DELETE, PATCH); safe methods (GET, HEAD, OPTIONS, TRACE) are exempt. The token comes back on the `X-CSRF-TOKEN` header or `_csrf` form parameter and is compared against the `CsrfTokenRepository` (by default `HttpSessionCsrfTokenRepository`, stored server-side in the session). Because the attacker's origin cannot read your token, forged requests are rejected with 403. CSRF protection is enabled automatically in Spring Security; you configure it via `http.csrf(...)` in the `SecurityFilterChain`.

code

java · 14 lines
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
            .formLogin(Customizer.withDefaults())
            // CSRF is ON by default; this shows explicit, default synchronizer-token config
            .csrf(Customizer.withDefaults());
        return http.build();
    }
}

go deeper

for a junior

Know the one-sentence definition, that browsers auto-send cookies, and that Spring uses a token on state-changing requests.

for a middle

Explain the synchronizer-token pattern, which methods are protected, and the CsrfFilter/CsrfTokenRepository roles.

for a senior

Discuss default HttpSession storage vs cookie repo tradeoffs and how tokens reach a JS client.

for a principal

Frame CSRF as an ambient-credential problem and connect the defense choice to the app's auth model and deployment topology.

## What CSRF is **Cross-Site Request Forgery (CSRF)** is an attack where a malicious web page causes the victim's browser to send a state-changing request to a site the victim is already authenticated to. It works because browsers **automatically attach ambient credentials** — cookies, HTTP Basic auth, TLS client certs — to every request to a domain, regardless of which page initiated it. If `evil.com` embeds `<form action="https://bank.com/transfer" method="post">` and auto-submits it, the victim's `bank.com` session cookie rides along and the transfer executes as the victim. Key enabling condition: **authentication via something the browser sends automatically** (a cookie). If auth requires an explicit header the attacker's page cannot set cross-origin, CSRF does not apply (see the bearer-token question). ## How Spring Security defends: the synchronizer token pattern Spring Security inserts a **`CsrfFilter`** into the filter chain. The idea: the server issues a **secret, unpredictable token** that the legitimate frontend receives and echoes back on every unsafe request. The attacker's page cannot read this token (same-origin policy blocks reading your responses), so it cannot forge a valid request. - **`CsrfToken`** — an object holding the token value, the header name (`X-CSRF-TOKEN`), and the parameter name (`_csrf`). - **`CsrfTokenRepository`** — where the token is stored/loaded. Default is **`HttpSessionCsrfTokenRepository`** (server-side in the `HttpSession`). Alternative: **`CookieCsrfTokenRepository`** (double-submit cookie, stateless). - **`CsrfFilter`** — on each request, if the method **requires** a token, it loads the expected token from the repository and compares it to the value supplied on the request (header or `_csrf` parameter). On mismatch it throws `InvalidCsrfTokenException`; if none supplied, `MissingCsrfTokenException`. Both surface as **HTTP 403** via the `AccessDeniedHandler`. ## Which methods are protected By `DefaultRequiresCsrfMatcher`, tokens are required only for **unsafe methods**: `POST`, `PUT`, `DELETE`, `PATCH`. **Safe/idempotent methods** — `GET`, `HEAD`, `OPTIONS`, `TRACE` — are exempt because they should not change state. (This is also why using GET for mutations is a security bug: it bypasses CSRF protection.) ## Enabled by default With the Spring Security starter, CSRF protection is **on by default**. For classic server-rendered apps (Thymeleaf forms), the token is auto-injected into forms, so it "just works". You only touch it when building SPAs/JS clients (you must read and resend the token) or when you legitimately disable it (stateless bearer APIs). ## Configuration entry point ```java http.csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())); ``` ## Gotchas - Disabling CSRF wholesale "to make POSTs work" while still using cookie auth **reopens the hole** — the classic beginner mistake. - GET endpoints that mutate state escape CSRF checks entirely. - SPAs must explicitly read the token (from cookie or a bootstrap endpoint) and set the `X-CSRF-TOKEN` header.

  • Why are GET requests exempt from CSRF checks?
    GET is defined as safe/idempotent and must not change state, so there is nothing to forge. The corollary: never perform mutations on GET, because doing so silently bypasses CSRF protection.
  • Where is the token stored by default, and what does that imply?
    In the HttpSession via HttpSessionCsrfTokenRepository, so the default synchronizer-token approach requires server-side session state; that matters for horizontally scaled/stateless deployments.

saying these in an interview costs you the question

  • Claiming CSRF lets an attacker read the victim's data (it only causes writes; same-origin policy still blocks reading responses)
  • Saying HTTPS or CORS alone prevents CSRF
  • Believing GET requests are also protected by the token
  • Thinking disabling CSRF is always safe

context

open as a page

When is it correct to disable CSRF protection for a stateless bearer-token API, and why is it safe there but dangerous with cookie auth?

level: seniorimportance: must knowfreq 76%

basics

~20 s

Disable CSRF only when auth uses a bearer token in the Authorization header and no cookies. Browsers don't auto-attach that header cross-site, so there's nothing to forge. If auth uses cookies (session or JWT-in-cookie), keep CSRF on.

open as a page

In Spring Security 6, how does CSRF token handling work (deferred tokens, XorCsrfTokenRequestAttributeHandler, BREACH), and what breaks in a SPA if you don't account for it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Spring Security 6 defers (lazily loads) the CsrfToken and, by default, XORs it with random bytes per request via XorCsrfTokenRequestAttributeHandler for BREACH protection. In a SPA this means the cookie holds the raw token but the header must be handled correctly, so you often add a filter to force token loading and a request handler that resolves the plain header value.

open as a page

How do SameSite cookies and CSRF tokens relate as defenses, and why is SameSite not a full replacement for Spring's CSRF protection?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

SameSite=Lax/Strict tells the browser not to send the cookie on cross-site requests, blocking most CSRF at the cookie layer. But it's browser-dependent, Lax still allows top-level GET navigations, and coverage varies, so keep Spring's CSRF token as defense in depth.

open as a page