Cross-Site Request Forgery (CSRF)
Cross-Site Request Forgery (CSRF)
What it is
CWE-352 occurs when a web application accepts state-changing requests from a user without verifying that the user actually intended to make the request. An attacker hosts a page that issues a request to the target site; if the victim is authenticated and the target site does not check origin, referrer, or a CSRF token, the request executes with the victim's credentials.
Why it matters
CSRF is one of the longest-standing entries in the OWASP Top 10 (most recently consolidated under A01:2021 Broken Access Control). Successful exploitation lets an attacker transfer funds, change passwords, add admin users, post content, or trigger any other state change the victim is authorized to perform. Modern browsers ship SameSite=Lax cookies by default, which mitigates many cross-origin POST attacks, but GET-based state changes and same-site iframes remain exploitable when CSRF tokens are missing.
Common patterns
- •State-changing endpoints (POST, PUT, DELETE, PATCH) without CSRF token validation.
- •Cookies issued without the SameSite attribute, or with SameSite=None and no Secure flag.
- •Endpoints that perform state changes on GET (a CWE-650 overlap).
- •Custom session middleware that forgets to verify a per-session CSRF token.
- •API endpoints that authenticate via cookies but accept cross-origin requests via permissive CORS.
Languages affected
What Deva detects
Deva flags Express, Koa, Flask, Django, and Rails route handlers that perform writes without a CSRF middleware (csurf, lusca, django-csrf, Rails protect_from_forgery). The scanner also detects cookie-set calls that omit SameSite, and CORS configurations that allow credentials with a wildcard origin. For SPAs that use Authorization headers instead of cookies, Deva treats the route as exempt because tokens are not auto-attached by browsers.
Example
Vulnerable
import express from 'express'
import session from 'express-session'
const app = express()
app.use(session({ secret: 'k', resave: false, saveUninitialized: false }))
app.post('/account/email', (req, res) => {
req.session.user.email = req.body.email
res.json({ ok: true })
})Fixed
import express from 'express'
import session from 'express-session'
import csurf from 'csurf'
const app = express()
app.use(session({
secret: 'k',
resave: false,
saveUninitialized: false,
cookie: { sameSite: 'lax', secure: true, httpOnly: true },
}))
app.use(csurf())
app.get('/csrf-token', (req, res) => {
res.json({ token: req.csrfToken() })
})
app.post('/account/email', (req, res) => {
// csurf middleware already validated req.body._csrf
req.session.user.email = req.body.email
res.json({ ok: true })
})Explanation
The vulnerable version accepts a POST to change the account email without any anti-forgery defense. An attacker's page issues a cross-origin POST with the victim's cookies attached and changes the email. The fix sets SameSite=Lax (defense in depth), requires CSRF tokens on writes, and exposes a /csrf-token endpoint so the SPA can attach the token to subsequent POSTs.
Where this fits in OWASP Top 10
Compliance framework mapping
| Framework | Controls |
|---|---|
| OWASP Top 10 (2021) | A01:2021 Broken Access Control |
| NIST 800-53 Rev 5 | AC-4 Information Flow EnforcementSI-10 Information Input Validation |
| PCI-DSS v4.0 | 6.2.4 Software engineering techniques |
| CMMC 2.0 L2 | AC.L2-3.1.3 CUI flow control |
Related CWEs
Deva detects CWE-352 alongside 970+ other CWE patterns at write time, with AI-assisted fix generation that maintains compliance.