Back to scan results
What this check probes
For each POST form found, we look for any hidden input whose name matches one of:
csrf_token, csrfmiddlewaretoken (Django)
authenticity_token (Rails)
_token, _csrf (Laravel, Express csurf)
__RequestVerificationToken (ASP.NET)
X-CSRF-Token when injected as a meta tag for AJAX use
nonce, state, xsrf, antiforgery — generic patterns
Forms with no token are flagged as a warning — the absence of a token doesn't prove CSRF vulnerability (the framework might enforce it via cookie+header instead, or the form might be intentionally cross-origin like a public newsletter signup), but it's worth investigating.
Forms with method="get" are not checked — GET should never have side effects.
Why this matters for PCI DSS
Cross-Site Request Forgery: an attacker hosts a page that triggers a state-changing request to your site. The user, who is logged in to your site in another tab, has cookies sent automatically. Without an anti-CSRF token, your server can't distinguish between an intentional submission and a forged one.
For PCI scope: a CSRF on a "change shipping address" or "send refund" endpoint can be used to redirect goods or money to the attacker. A CSRF on the password change endpoint can be used to lock the legitimate user out and take over the account.
PCI DSS 4.0 Requirement 6.2.4 explicitly names cross-site request forgery as an attack class against which payment systems must defend.
How to fix it
Modern frameworks all ship CSRF protection — turn it on and use the helper:
Django — {% csrf_token %} in every form template; django.middleware.csrf.CsrfViewMiddleware in MIDDLEWARE.
Rails — protect_from_forgery with: :exception in ApplicationController; the form_with helper inserts the token automatically.
Laravel — Blade @csrf directive in every form; the VerifyCsrfToken middleware enforces.
ASP.NET MVC / Razor Pages — @Html.AntiForgeryToken() in the form; [ValidateAntiForgeryToken] attribute on the action. ASP.NET Core enforces tokens automatically when antiforgery is configured.
Express (Node.js) — install csurf middleware; in the template render the token as a hidden field; in the AJAX flow set the X-CSRF-Token header.
For SPAs and APIs, the standard pattern is the "double-submit cookie": the server sets a CSRF cookie on first response; the SPA reads it and copies the value into a custom header (e.g., X-CSRF-Token) on each request. Same-origin policy stops cross-site pages from reading the cookie or setting the header, so forgery fails.
Defense in depth — set SameSite=Lax or Strict on session cookies (see Check 14). Modern browsers default to Lax, which blocks most forms of CSRF before the request even leaves the browser. SameSite is not a substitute for tokens but a strong second layer.
Don't use referer-checking alone — Referer headers can be stripped by intermediaries and are not trustworthy.