CWE-352: Cross-Site Request Forgery (CSRF) — Exploiting Trust in the Browser

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:

  1. Browser submits request to target site
  2. Browser automatically includes victim’s session cookie
  3. Target site processes request as authenticated user
  4. Unauthorized action occurs

Visual: CSRF Attack Flow

1. Attacker Site Hosts Malicious Form/JS 2. Victim Browser Auto-Sends Cookies 3. Target App Accepts Request 4. Result Unauthorized Action

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

CSRF Funds Transfer Privilege Changes Account Takeover Prep Persistent Compromise

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