The Request You Never Made: Understanding CSRF
Your browser can be authenticated and still send a request you never intended to make.
The session is valid.
The connection is encrypted.
The user is logged in.
Yet an application can still process an action the user never intentionally requested.
This is where Cross-Site Request Forgery (CSRF) becomes important.
CSRF is not about stealing a password.
It is about abusing the trust between a browser and an application.
The Core Concept
Modern web applications often use cookies to maintain authenticated sessions.
Once a user logs in, the browser stores a session cookie and automatically sends it with requests to the application's domain.
For example:
User
↓
Login
↓
Session Cookie
↓
Browser
↓
Authenticated Requests
This is convenient.
But convenience creates an important security question:
What happens when another website causes the browser to send a request?
If the server only checks whether the session cookie is valid, it may see a perfectly authenticated request.
The server knows who the request belongs to.
It may not know whether the request was intentionally initiated by the user.
That is the core problem CSRF addresses.
How CSRF Works
A simplified CSRF scenario looks like this:
User logs into Application
↓
Browser receives session cookie
↓
User visits another website
↓
Another site causes a request
↓
Browser sends credentials when applicable
↓
Application receives the request
↓
Server processes the action
The dangerous part is the trust relationship.
The browser is behaving normally.
The server is behaving normally.
But the request may not represent an action the user intended to perform.
This is why CSRF is fundamentally a request-trust problem.
Why Authentication Alone Isn't Enough
Authentication answers:
Who is this user?
CSRF protection addresses a different question:
Should this state-changing request be trusted?
Those are not the same thing.
Consider:
Authentication
↓
"This request belongs to the authenticated user."
That does not automatically mean:
"This request was intentionally made by the user."
A secure application needs to reason about both.
Identity is not the same thing as request intent.
The Browser Is Part of the Security Model
One of the easiest ways to understand CSRF is to understand how browsers handle credentials.
Browsers manage things such as:
- Cookies
- Sessions
- Origins
- Cross-site requests
- Credential policies
For example:
POST /api/account/settings
Cookie: session=abc123
The server might interpret this as:
Valid Session
↓
Authenticated User
↓
Process Request
But the presence of a valid session does not prove that the request originated from the application's own interface.
The browser can carry authentication credentials automatically.
That behavior is useful for normal applications.
It is also one of the reasons CSRF exists.
Protecting State-Changing Requests
CSRF primarily matters when a request changes application state.
For example:
POST /api/users
PATCH /api/users/123
DELETE /api/users/123
POST /api/payments
PATCH /api/settings
These operations can modify data or trigger important actions.
A useful engineering rule is:
Treat every mutation as a security boundary.
A typical request pipeline might look like this:
Incoming Request
↓
Rate Limiting
↓
Origin / CSRF Verification
↓
Authentication
↓
Authorization
↓
Input Validation
↓
Business Logic
↓
Database
↓
Response
Security checks should happen before sensitive business logic executes.
CSRF Tokens
One common defense is the CSRF token.
The legitimate application includes a value with state-changing requests, and the server verifies that value before processing the operation.
For example:
POST /api/profile
Cookie: session=abc123
X-CSRF-Token: random-value
Conceptually:
Session
+
CSRF Token
↓
Request Verification
↓
Continue
The important security property is not the parameter name.
The token must be generated and validated in a way that prevents an untrusted site from reliably obtaining or predicting the value required by the application.
This creates an additional trust signal beyond the authentication cookie.
SameSite Cookies
Modern browsers also provide the SameSite cookie attribute.
For example:
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax
SameSite controls when cookies are sent in cross-site contexts.
Common settings include:
Strict
Lax
None
For many applications, Strict or Lax can reduce common CSRF scenarios.
However, cookie configuration must match the application's architecture.
Different authentication flows can have different requirements.
Security settings should follow the application's actual trust model, not configuration copied from another project.
Origin Validation
Another useful defense is validating the request's origin.
For example:
Request
↓
Origin Header
↓
Expected Origin?
├── Yes → Continue
└── No → Reject
An application might define a trusted origin such as:
https://app.example.com
If a sensitive mutation arrives with an unexpected origin, the server can reject it before executing business logic.
This creates a clear security boundary:
Trusted Origin
↓
Security Checks
↓
Business Logic
For applications that can reliably validate request origins, this can be an important layer of defense.
CSRF vs XSS
CSRF and XSS are often mentioned together, but they are different vulnerabilities.
| Vulnerability | Core Problem | Main Risk |
|---|---|---|
| CSRF | Cross-site request abuse | Unwanted authenticated actions |
| XSS | Untrusted code execution | Attacker-controlled code |
| Authentication flaw | Broken identity verification | Unauthorized account access |
| Authorization flaw | Broken permission checks | Unauthorized operations |
The distinction matters.
A CSRF token does not automatically fix XSS.
An XSS fix does not automatically eliminate every CSRF scenario.
Security controls should target specific threats.
Where Engineers Get It Wrong
One common mistake is treating CSRF as a frontend problem.
For example:
Frontend
↓
CSRF Token
↓
API
If the API never validates the token, the protection is meaningless.
The server must enforce the security decision:
Client
↓
Request
↓
Server Security Layer
↓
Validate
↓
Accept / Reject
Another common mistake is assuming HTTPS prevents CSRF.
HTTPS protects the communication channel.
It does not determine whether another website caused the browser to send a request.
Another mistake is assuming authentication is enough.
It is not.
A valid session proves identity. It does not automatically prove that the request should be trusted.
A Modern Mutation Pipeline
A production application should treat security as a layered system.
For example:
User
↓
Browser
↓
HTTPS
↓
Rate Limiting
↓
Origin / CSRF Protection
↓
Authentication
↓
Authorization
↓
Schema Validation
↓
Business Logic
↓
Database Transaction
↓
Audit Logging
↓
Response
Each layer answers a different question.
Rate Limiting → How often can this happen?
CSRF Protection → Is this request context trusted?
Authentication → Who is making the request?
Authorization → Are they allowed to do this?
Validation → Is the input valid?
Business Logic → Should this operation happen?
Database → How is the state persisted?
Audit Logging → What happened?
This is what mature security architecture looks like.
Not one magical security feature.
Multiple controls working together.
Designing Around the Threat
When building a cookie-authenticated application, engineers should think about the entire request lifecycle.
Request
↓
Is it a mutation?
↓
Does it use ambient authentication credentials?
↓
Is the request context trusted?
↓
Does CSRF protection apply?
↓
Is the user authenticated?
↓
Is the user authorized?
↓
Is the input valid?
↓
Execute
This approach is more reliable than adding security controls after the application is already built.
Security should be part of the architecture.
Not an afterthought.
The Engineering Trade-Off
Security introduces complexity.
CSRF tokens require implementation and validation.
Origin checks require a clearly defined trust boundary.
Cookie policies must match authentication behavior.
Additional middleware adds another layer to the request pipeline.
But removing security because it creates complexity is usually the wrong optimization.
The better question is:
Where should the complexity live?
Instead of repeating security logic across every endpoint:
Route A → Custom Check
Route B → Custom Check
Route C → Forgot Check
Route D → Different Check
centralize common protections:
All Mutations
↓
Security Boundary
↓
Validated Request
↓
Route Handler
Centralized security controls reduce inconsistent implementations.
What Engineers Should Do
For a modern web application:
- Protect state-changing operations.
- Configure cookies with appropriate
Secure,HttpOnly, andSameSitesettings. - Use CSRF tokens where the authentication architecture requires them.
- Validate trusted origins for sensitive mutations.
- Never rely on frontend-only security checks.
- Keep authentication separate from authorization.
- Validate request bodies with schemas.
- Rate-limit sensitive endpoints.
- Log important administrative actions.
- Reject invalid requests early.
- Centralize reusable security logic.
And remember:
Do not confuse an authenticated request with a trusted request.
The Bigger Picture
CSRF teaches a much bigger lesson about web security.
Security is rarely about one feature.
It is about understanding trust boundaries.
Browser
↓
Network
↓
Application
↓
Authentication
↓
Authorization
↓
Business Logic
↓
Database
Every boundary introduces assumptions.
Every assumption should be questioned.
Who can send the request?
What credentials are attached?
Where did it originate?
What permissions does the user have?
What happens if the request is unexpected?
These questions lead to better architecture.
Conclusion
CSRF is not really about a mysterious browser bug.
It is about trusting a request simply because it carries valid credentials.
A browser can be authenticated.
A session can be valid.
HTTPS can be enabled.
And the request can still be something the user never intended to make.
That is why secure applications verify more than identity.
They verify request context, origin, authorization, and the security properties appropriate to their architecture.
Authentication tells you who the user is. Security engineering determines whether the request should be trusted.
Protect your mutations.
Understand your browser.
Design your trust boundaries.
And never assume that "authenticated" means "safe to execute."
