If you’ve ever built a web application or configured Spring Security, you’ve almost certainly encountered Cross-Site Request Forgery (CSRF).
In my previous guide, How OAuth 2.0 Works: A Practical Guide for Backend Developers, I briefly touched on the mysterious state parameter and noted that its core purpose is protecting authorization flows against CSRF attacks.
At the time, we treated CSRF as a quick prerequisite concept. Today, we’re taking a much deeper dive.
Perhaps you were building a REST API in Spring Boot, ran into unexpected HTTP 403 Forbidden errors on every POST request, and “fixed” it by adding .csrf(csrf -> csrf.disable()) to your Security Filter Chain.
Most tutorials treat CSRF as a checkbox item or a framework toggle. They immediately jump to code:
// What most tutorials show on line 1:
http.csrf(Customizer.withDefaults());
Starting with framework configuration hides how web security actually operates. Spring Security doesn’t invent security rules out of thin air. It responds to the fundamental mechanics of web browsers, HTTP protocols, and cookies.
In this handbook, we’ll take a bottom-up, first-principles approach. We won’t talk about Spring Security until we’ve thoroughly explored browsers, HTTP headers, session management, and the underlying mechanics of Cross-Site Request Forgery.
By the end of this guide, you’ll understand:
-
Why browsers automatically attach credentials to outgoing requests.
-
Why that automatic behavior creates a fundamental vulnerability.
-
Why attackers never need to steal or read your cookies to exploit CSRF.
-
Why Same Origin Policy (SOP) and CORS don’t prevent CSRF.
-
How modern defenses, from CSRF Tokens to SameSite cookies, work under the hood.
-
How Spring Security implements these defenses internally and how to configure them effectively.
Let’s begin by stripping away frameworks and looking at how the web actually works.
Table of Contents
The Problem Before CSRF
To understand security, we must first understand state.
The Hypertext Transfer Protocol (HTTP) is inherently stateless. This means that if Alice sends an HTTP request to travelbuddy.com (our example) at 10:00 AM, and sends another HTTP request to travelbuddy.com at 10:01 AM, the server treats those two requests as completely isolated, unrelated events.
Without a mechanism to remember Alice between requests, Alice would have to send her username and password inside every single HTTP request she makes. That would be horrific for both user experience and performance.
Before session mechanisms were standard, developers tried passing credentials via query parameters or basic authentication headers on every click. This led to credential exposure in server logs, browser histories, and URL shares.
How Do Sessions and Cookies Solve This?
To solve this, web engineers introduced the concept of Server-Side Sessions and HTTP Cookies.
When Alice logs into TravelBuddy by sending her username and password via a POST request to https://travelbuddy.com/login, the server verifies her credentials. Instead of asking Alice to log in again on the next page, the server creates a Session in its memory (or in a database/Redis cache) and assigns it a unique, unpredictable identifier: a Session ID.
The server then sends this Session ID back to Alice’s browser using a special HTTP response header: Set-Cookie.
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: JSESSIONID=abc123xyz789; Path=/; Secure; HttpOnly
When Alice’s browser receives this response, it sees the Set-Cookie header. It extracts JSESSIONID=abc123xyz789 and stores it inside its internal storage unit: the Browser Cookie Jar.
Now, Alice is “logged in”. The server remembers her via that session record, and the browser holds the key (JSESSIONID) to that session.
Why Browsers Automatically Send Cookies
Now we arrive at the pivotal design choice made in the early days of the web.
Once the browser stores JSESSIONID=abc123xyz789 in its cookie jar for the domain travelbuddy.com, how does that cookie get sent back to the server on subsequent requests?
Does the developer have to write custom JavaScript to attach the cookie? No.
Browsers are explicitly designed to handle cookie management automatically.
The Request Lifecycle and Automatic Cookie Attachment
Every time Alice’s browser prepares an HTTP request to https://travelbuddy.com (whether caused by Alice clicking a link, submitting an HTML form, or JavaScript triggering a fetch() call), the browser follows this exact process:
-
URL Inspection: The browser examines the destination URL (for example, https://travelbuddy.com/api/connections).
-
Cookie Jar Lookup: The browser scans its cookie jar for any stored cookies whose domain and path match travelbuddy.com.
-
Validation Check: It verifies if the cookie has expired, and if flags like Secure (requires HTTPS) are respected.
-
Header Injection: If valid cookies match, the browser automatically injects a Cookie header into the outgoing HTTP request payload.
Here’s what the outgoing request looks like as it leaves Alice’s machine:
POST /api/connections/add HTTP/1.1
Host: travelbuddy.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: text/html,application/xhtml+xml
Cookie: JSESSIONID=abc123xyz789
Content-Type: application/x-www-form-urlencoded
service=SkyScanner
Notice something critical: Neither Alice nor any custom frontend JavaScript explicitly attached Cookie: JSESSIONID=abc123xyz789.
The browser’s internal engine attached it automatically before sending the byte stream across the network. From the server’s perspective, receiving Cookie: JSESSIONID=abc123xyz789 is proof that the request originated from an authenticated session belonging to Alice.
This automatic behavior is convenient. It makes web browsing seamless across page reloads and link navigation. But as we’ll soon see, this convenience leaves a backdoor wide open.
When Automatic Cookies Become Dangerous
Is automatic cookie inclusion a vulnerability by itself?
No. If Alice only visits travelbuddy.com, automatic cookie inclusion works exactly as intended.
The vulnerability emerges because of a simple web reality: Alice visits multiple websites in the same browser session.
Enter evil.com
Suppose Alice is logged into TravelBuddy in Tab 1. Her session cookie (JSESSIONID=abc123xyz789) sits safely inside her browser’s cookie jar for travelbuddy.com.
In Tab 2, Alice visits an unrelated website: https://evil.com (perhaps she clicked a link in a phishing email or a forum post).
evil.com is controlled by an attacker. The attacker knows that TravelBuddy has a feature located at POST [https://travelbuddy.com/api/connections/add that connects third-party services. The attacker wants to trick Alice into connecting the attacker’s malicious service to her account.
The attacker embeds the following hidden HTML form inside the HTML page served by evil.com:
Walkthrough of the Attack Execution
Let’s trace step-by-step what happens when Alice opens https://evil.com/win-a-car.html:
-
Alice’s browser fetches and parses HTML from evil.com.
-
The browser encounters the
Source link
![Browser Mechanics, Attacks, and Spring Security Implementation [Full Handbook] CSRF from Scratch: Browser Mechanics, Attacks, and Spring Security Implementation [Full Handbook]](https://targetsocialmedia.com/wp-content/uploads/2026/08/Browser-Mechanics-Attacks-and-Spring-Security-Implementation-Full-Handbook-1024x576.png)