Cross-Site Request Forgery (CSRF)
debt(d5/e3/b5/t5)
Closest to 'specialist tool catches' (d5). The term's detection_hints specify semgrep as the tool and note automated detection is 'no' — meaning standard linters won't catch it. SAST tools like semgrep can detect missing CSRF token verification patterns, but it requires deliberate configuration and isn't caught by default PHP tooling.
Closest to 'simple parameterised fix' (e3). The quick_fix specifies adding SameSite=Lax to session cookie and including a per-session CSRF token in forms. This is more than a one-line patch (requires middleware configuration, form updates, and potentially token validation logic) but is a well-understood pattern that doesn't require architectural changes — typically a few files in one component.
Closest to 'persistent productivity tax' (b5). CSRF protection must be applied consistently across all state-changing endpoints in the web context. The applies_to shows it affects all web PHP contexts. Every new form and state-changing route requires the developer to remember to include tokens, creating an ongoing tax. It shapes how you build forms but doesn't define the entire system architecture.
Closest to 'notable trap' (t5). The misconception field explicitly states developers believe 'HTTPS prevents CSRF' — a documented gotcha that most developers eventually learn is false. The common_mistakes list shows several additional traps (protecting only POST, presence-check-only validation, static tokens), but these are learnable through standard security training rather than being catastrophically counterintuitive.
Also Known As
TL;DR
Explanation
CSRF exploits the browser's automatic inclusion of session cookies. An attacker hosts a page with a hidden form or image tag that sends a state-changing request to the target site. The victim's browser attaches their valid session cookie, so the server treats it as legitimate. Mitigation requires a per-session, per-form unpredictable token that the attacker cannot know — validated server-side with a constant-time comparison.
How It's Exploited
<img src="https://yourbank.com/transfer?to=attacker&amount=9999">
Diagram
sequenceDiagram
participant V as Victim Browser
participant L as "Legit Bank<br/>bank.com"
participant A as "Attacker Site<br/>evil.com"
V->>L: Login to bank.com
L-->>V: Session cookie set
A->>V: Serve malicious page with hidden form
V->>L: "POST /transfer (auto-submit)<br/>Cookie sent automatically!"
L-->>L: Transfer executed - no CSRF token checked
Note over L: "Fix: require CSRF token in POST body<br/>that attacker cannot read"
Common Misconception
Why It Matters
Common Mistakes
- Protecting POST routes but forgetting PUT, PATCH, and DELETE — all state-changing methods need tokens.
- Validating the token exists but not validating it matches the session — presence check alone is useless.
- Disabling CSRF middleware globally in API routes and forgetting the app also serves browser clients.
- Using a static, never-rotating token — tokens must be per-session and ideally per-request.
Code Examples
// No CSRF token — any site can submit this form on behalf of the user
Route::post('/account/delete', [AccountController::class, 'destroy']);
// Laravel — CSRF middleware applied globally, @csrf in every form
<form method="POST" action="/account/delete">
@csrf
<button>Delete account</button>
</form>
// For APIs: use SameSite=Strict cookies + verify Origin header
if ($_SERVER['HTTP_ORIGIN'] !== 'https://yourapp.com') {
http_response_code(403); exit;
}