What is CSRF, and how does Spring Security protect against it by default?
answer
- browser auto-sends cookies cross-site
- CsrfFilter + secret token on POST/PUT/DELETE/PATCH
- GET/HEAD/OPTIONS/TRACE exempt
- HttpSessionCsrfTokenRepository default
- mismatch -> 403
basics
~10 sCSRF 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 sCross-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@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
Know the one-sentence definition, that browsers auto-send cookies, and that Spring uses a token on state-changing requests.
Explain the synchronizer-token pattern, which methods are protected, and the CsrfFilter/CsrfTokenRepository roles.
Discuss default HttpSession storage vs cookie repo tradeoffs and how tokens reach a JS client.
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