Cross-Site Request Forgery remains one of the most misunderstood web vulnerabilities in modern application security. Many developers believe CSRF is largely solved by SameSite cookies, single-page applications, or API-driven architectures. In reality, CSRF persists because browsers continue to automatically attach authentication credentials to requests, and developers continue to underestimate how many application flows still rely on that behavior.
CWE-352 occurs when a web application accepts a state-changing request from a user’s browser without verifying that the request was intentionally initiated by that user.
In practical terms:
The attacker causes the victim’s browser to perform authenticated actions the victim never intended.
This article breaks down how CSRF works, why developers still introduce it, modern exploitation techniques, framework-specific mitigations, and secure implementation patterns.
What Is CSRF?
CSRF exploits the browser’s habit of automatically including credentials with requests to trusted sites.
Those credentials may include:
- Session cookies
- Browser-managed client certificates
- HTTP authentication headers
- Some bearer tokens stored/accessed automatically by browser logic
If the application relies solely on those credentials to authorize state-changing actions, an attacker can trick the victim’s browser into sending authenticated requests.
How CSRF Actually Works
A victim is authenticated to a target site:
Cookie: session=abc123
The attacker hosts malicious HTML:
<form action="https://bank.example/transfer" method="POST">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="to" value="attacker">
</form>
<script>document.forms[0].submit()</script>
When the victim visits the attacker’s page:
- Browser submits request to target site
- Browser automatically includes victim’s session cookie
- Target site processes request as authenticated user
- Unauthorized action occurs
Visual: CSRF Attack Flow
Why Developers Still Get CSRF Wrong
SameSite Cookie Overconfidence
SameSite helps, but does not eliminate CSRF risk:
- Legacy browser support varies
- Misconfigurations are common
- Some flows require relaxed SameSite policies
- SameSite does not protect every credential mechanism
API / SPA Assumptions
Teams often assume APIs are immune.
If the browser automatically includes credentials, CSRF remains possible regardless of frontend architecture.
“POST Means Safe”
POST alone is not protection.
A forged POST request is still a valid CSRF attack.
Inconsistent Protection Coverage
Applications often protect:
- Main user forms
But forget:
- Admin endpoints
- API routes
- Legacy controllers
- Background AJAX endpoints
Modern Exploitation Techniques
Hidden Auto-Submitting Forms
Classic CSRF delivery mechanism.
JavaScript Fetch Abuse
Where CORS/configuration allows.
Image / Script / Link Triggering
GET-based state changes remain exploitable:
<img src="https://example.com/deleteAccount?id=5">
If state changes occur via GET, CSRF is trivial.
Multi-Step Workflow Abuse
Attackers may chain multiple forged requests.
Login CSRF
Force victim to authenticate into attacker-controlled account.
Can poison sessions and facilitate later abuse.
Visual: CSRF Exploitation Outcomes
Framework-Specific Mitigations
Synchronizer Token Pattern
Embed unpredictable token in forms:
<input type="hidden" name="csrf_token" value="{{token}}">
Server validates token before processing.
Double Submit Cookie Pattern
- Set CSRF cookie
- Require matching request parameter/header
- Validate equality server-side
Custom Header Requirements
Require non-simple header for state-changing API requests:
X-CSRF-Token: abc123
Browsers cannot send arbitrary custom headers cross-origin via plain form submission.
Framework Defaults
Django
Built-in CSRF middleware enabled by default.
Rails
Automatic authenticity tokens in forms.
Spring Security
Provides CSRF token protection.
ASP.NET Core
Supports antiforgery tokens.
Developers often disable these protections for convenience—creating risk.
Secure Coding Examples
Unsafe State Change via GET
@app.route('/delete-user')
def delete_user():
...
Safer POST + CSRF Validation
@app.route('/delete-user', methods=['POST'])
@csrf_protect
def delete_user():
...
SameSite Cookie Hardening
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Lax
Useful defense-in-depth, not sole protection.
Defense in Depth
Validate Origin / Referer Headers
Check request source matches expected origin.
Helpful but not foolproof.
Re-Authenticate for Critical Actions
Require password / MFA for:
- Password changes
- Email changes
- Financial transfers
- Privilege modifications
Eliminate State Changes via GET
GET should never mutate application state.
Final Thoughts
CSRF remains dangerous because it exploits browser trust rather than application logic flaws.
It persists because:
- Developers overtrust SameSite
- Framework protections are disabled
- Legacy endpoints are forgotten
- API architectures are wrongly assumed immune
The rule remains simple:
If the browser automatically sends credentials, and you do not verify user intent, CSRF is possible.
Authentication proves who the user is.
CSRF protection proves the user meant to perform the action.
Coming Next
CWE-22 — Path Traversal
Including:
- How attackers escape intended file system boundaries
- Modern traversal payload techniques
- Archive extraction and upload abuse
- Framework-specific mitigations
- Secure file handling patterns

2 thoughts on “CWE-352: Cross-Site Request Forgery (CSRF) — Exploiting Trust in the Browser”