November 4, 2026
Updated: November 4, 2026
How CSRF works, what SameSite defaults changed by browser, login and JSON CSRF, real CVEs, layered prevention and lab-safe testing.
Mohammed Khalil

Cross-site request forgery (CSRF) is an attack that makes a logged-in user's browser send a request the user never intended, such as changing an email address or adding an admin. Chromium-based browsers now hold back many cross-site cookies by default, but CSRF has not gone away. This guide explains how a CSRF attack works, what SameSite=Lax defaults really changed, how login CSRF and JSON APIs fit in, and which real CVEs show the pattern. It also covers how testers check for CSRF and the controls that prevent it.
CSRF is a web vulnerability in which a malicious site causes a victim's browser to send an authenticated, state-changing request to another site where the victim is logged in. The target server sees a valid session cookie and processes the request as if the user meant it. MITRE catalogs it as CWE-352: Cross-Site Request Forgery.
It is also called XSRF or "sea-surf." MDN's CSRF guide sums up the three conditions that make a site vulnerable:
Remove any one of those conditions and the attack fails. Every CSRF defense in this guide works by breaking one of them.
CSRF does not have its own category in the current list. The OWASP Top 10:2025 A01 Broken Access Control page names CWE-352 among its notable weaknesses, next to server-side request forgery. That fits the bug: the server performs an action without confirming that the user actually asked for it. Our OWASP Top 10 guide walks through every 2025 category.
The example below is illustrative and uses reserved example domains. Imagine a web app at app.example.com that lets signed-in users change their account email with a simple form. The browser sends this request:
POST /account/email HTTP/1.1
Host: app.example.com
Cookie: session=<session-id>
Content-Type: application/x-www-form-urlencoded
email=user%40example.comThe server checks the session cookie, finds a logged-in user and updates the email. Nothing else in the request proves that the user clicked the button.
Now the attack, step by step:
app.example.com, and the browser stores the session cookie.attacker.example, through a link in an email, an ad or a forum post.app.example.com/account/email with an email address the attacker controls. A conceptual version looks like this:<!-- Illustrative only: page hosted on attacker.example -->
<form action="https://app.example.com/account/email" method="POST">
<input type="hidden" name="email" value="attacker@example.net">
</form>
<!-- A real attack submits this automatically; a lab test uses a visible button. -->
Notice what the attacker never touches. They do not steal the cookie, and the same-origin policy stops their page from reading the response. CSRF is a "blind" write: the attacker chooses the action, and the victim's browser supplies the identity.
| Question | Answer | Why |
|---|---|---|
| Can it change data as the victim? | Yes | The browser attaches the victim's cookies to the forged request |
| Can it read the response or page data? | No, not on its own | The same-origin policy blocks the attacker's page from reading cross-origin responses |
| Can it steal the session cookie? | No | The cookie is never exposed to the attacker's page |
| Does it need the victim to do anything? | Usually only to visit a page while logged in | Forms and navigations can be triggered by the page itself |
| Can it hit admin functions? | Yes, if an admin is the victim | Admin sessions make CSRF far more serious, as the cases below show |
| Can a misconfigured CORS policy make it worse? | Yes | A policy that trusts the attacker's origin with credentials lets the attacker read responses too |
Typical impact therefore depends on what the forged action does. Changing an email, password-recovery phone number or MFA setting can lead to account takeover. On an admin session it can create users, change security settings or, on some devices, run commands.
For most of the web's history, browsers attached cookies to every request to a site, wherever it came from. That is exactly what CSRF exploits. The SameSite cookie attribute changed the rules by letting a site say when its cookies may travel with cross-site requests. According to MDN's Set-Cookie reference:
| SameSite value | Cookie sent on cross-site requests? | CSRF effect |
|---|---|---|
| Strict | Never | Blocks classic cross-site CSRF; may log users out when they follow links from other sites |
| Lax | Only on top-level navigations that use a safe method such as GET | Blocks cross-site POST forms, image tags, fetch calls and iframes; still allows a cross-site link to load a page |
| None | Always; the Secure attribute is required | No CSRF protection from the browser |
Third-party cookie blocking in some browsers can also stop None cookies, but that is a privacy feature you do not control. A "site" here is the registrable domain plus scheme, such as https://example.com. It is broader than an origin: shop.example.com and blog.example.com are different origins but the same site.
The big shift was making Lax the default for cookies that do not declare SameSite at all. Before that, a developer who did nothing got no protection. Google's SameSite updates page for Chromium records a bumpy rollout:
Other browsers did not all follow. MDN's browser compatibility data, checked on November 5, 2026, shows this:
| Browser | Cookies without SameSite treated as Lax? | SameSite=None requires Secure? |
|---|---|---|
| Chrome | Yes, since version 80 | Yes, since version 80 |
| Edge | Yes, since version 86 | Yes, since version 86 |
| Firefox | Only behind the network.cookie.sameSite.laxByDefault preference; not the shipped default | Yes, since version 131 |
| Safari | Not listed as supported | Not listed as supported |
The source data is the open MDN browser-compat-data project on GitHub. In plain terms, "browsers default to Lax" is only true for Chromium-based browsers. A Firefox or Safari user may still send a session cookie that has no SameSite attribute on a cross-site POST.

Chromium added a compatibility allowance it calls "Lax + POST." A cookie that has no SameSite attribute and is at most two minutes old can still be sent on a top-level cross-site POST. Chromium described the window as temporary, but MDN's Set-Cookie reference still documents it as of November 2026. A cookie explicitly set to SameSite=Lax does not get this allowance. That is one more reason to set the attribute yourself rather than rely on the default.
The default raised the bar for lazy CSRF, and many old bugs stopped working in Chrome. But it does not protect every app, and the OWASP and MDN guidance both treat SameSite as defense in depth, not a complete fix. Gaps that remain:
GET /account/delete is still forgeable. Frameworks that let a GET be treated as a POST through a method-override parameter widen this gap.SameSite=None. Single sign-on, embedded widgets and cross-site APIs often need None, and those cookies get no browser help.Most CSRF talk is about forging actions inside a victim's session. Login CSRF works the other way around: the attacker forges a request to the login form with the attacker's own credentials, so the victim ends up signed in as the attacker.
The variant was named in 2008 by Stanford researchers Adam Barth, Collin Jackson and John C. Mitchell in "Robust Defenses for Cross-Site Request Forgery", presented at ACM CCS 2008. They reported that Yahoo, PayPal and Google were vulnerable at the time. The harm comes afterward. If the victim then saves a card, enters searches or uploads files, that data lands in an account the attacker controls. The same paper proposed the Origin request header, which browsers later adopted and which is now a core CSRF defense.
Login CSRF is easy to miss because the login form runs before there is a session to attach a token to. The OWASP CSRF Prevention Cheat Sheet recommends a pre-session that carries a token in the login form, then destroying it and issuing a new session after authentication to avoid session fixation.
Single sign-on adds its own version. In OAuth 2.0, an attacker can inject their own authorization code into a victim's callback so the victim's app links to the attacker's account. RFC 6749, Section 10.12 describes this as CSRF against the redirection URI. The current best practice, RFC 9700 (January 2025), says the long-established countermeasure is a random state value acting as a CSRF token, and that PKCE or the OpenID Connect nonce provides the same protection. A missing or unchecked state is still a common finding.
A common belief is that JSON APIs are immune to CSRF because an HTML form cannot send application/json. It is partly true, and the gaps are where real bugs live.
The key idea is the "simple request." An HTML form can send only three content types: application/x-www-form-urlencoded, multipart/form-data and text/plain. Requests like these go cross-origin without a CORS preflight. A request with Content-Type: application/json or a custom header is not simple, so the browser first asks the server for permission with a preflight. MDN's CSRF guide explains that a site using fetch() can defend itself by making sure state-changing requests are never simple requests.
| API behavior | CSRF risk | Why |
|---|---|---|
| Requires Content-Type: application/json and rejects everything else | Low | A cross-site form cannot send it, and fetch triggers a preflight the server can refuse |
| Parses the body as JSON whatever the Content-Type says | High | A form can send text/plain whose body happens to be valid JSON, with no preflight |
| Also accepts form-encoded bodies for the same action | High | A plain cross-site form works |
| Requires a custom header such as X-CSRF-Token | Low | Browsers send custom headers cross-origin only after a successful preflight |
| Authenticates with a bearer token in the Authorization header | Low for CSRF | The browser does not attach it automatically, so a forged request has no credentials |
| CORS reflects any Origin and sends Access-Control-Allow-Credentials: true | Critical | The attacker's page can send credentialed requests and read the responses, including any CSRF token |
That last row is the most important. CORS does not create CSRF protection; it relaxes the same-origin policy. MDN warns that a state-changing response is exposed to an origin when it lists that origin in Access-Control-Allow-Origin and also allows credentials. OWASP recommends allowing only a few specific origins you control and avoiding regex allowlists that cover every subdomain.
GraphQL endpoints are a frequent example, because one /graphql endpoint often accepts several content types and sometimes GET mutations. Our GraphQL API vulnerabilities guide covers the GraphQL-specific conditions. WebSockets have a close relative, cross-site WebSocket hijacking, explained in our WebSocket vulnerabilities guide.
CSRF is not only a textbook bug. A query of the NVD API on November 5, 2026 returned five CVEs mapped to CWE-352 that CISA lists in its Known Exploited Vulnerabilities (KEV) catalog. Four of them, with one DeepStrike research case, show the pattern:
| CVE | Product | What a forged request could do (per NVD) | NVD published | Added to CISA KEV |
|---|---|---|---|---|
| CVE-2008-4128 | Cisco IOS 12.4 HTTP administration on the 871 router | Execute arbitrary commands through the web admin interface | September 18, 2008 | July 13, 2026 |
| CVE-2014-100005 | D-Link DIR-600 router (rev. Bx) before firmware 2.17b02 | Create an administrator account or enable remote management | January 13, 2015 | May 16, 2024 |
| CVE-2020-10181 | Sumavision Enhanced Multimedia Router 3.0.4.27 | Create users with administrator privileges | March 11, 2020 | November 3, 2021 |
| CVE-2023-2533 | PaperCut NG/MF | Alter security settings or execute arbitrary code if the victim is a logged-in admin | June 20, 2023 | July 28, 2025 |
| CVE-2025-63724 | SVX Portal 2.7A | Overwrite any portal setting from an admin's browser (see below) | November 14, 2025 | Not listed |
NVD records for each: CVE-2008-4128, CVE-2014-100005, CVE-2020-10181, CVE-2023-2533 and CVE-2025-63724.
Three lessons stand out:
DeepStrike published a technical write-up on SVX Portal 2.7A and CVE-2025-63724, and NVD lists that write-up as a reference for the CVE. The case is a useful lesson in classification and in how browser defaults decide real-world exposure.
admin/update_setings.php and maps it to CWE-89. The record lists a CVSS 3.1 base score of 6.0.session.cookie_samesite setting is empty by default, which means no SameSite attribute is set, according to the PHP session configuration manual. In Chrome or Edge that cookie is treated as Lax and left off cross-site POSTs, apart from the two-minute allowance. In Firefox or Safari it is sent. The same deployment can therefore be exploitable for one admin and not for another, depending on their browser.The fix in the advisory is the general fix: an allowlist of editable settings, a CSRF token on the settings form, prepared statements, and logging of every configuration change.
No single control covers every case. The reliable approach is layered: a token or origin check that the server enforces, cookies that the browser restricts, and request design that leaves no simple path. The OWASP CSRF Prevention Cheat Sheet is the best single reference and underpins the advice below. For how CSRF fits alongside authentication, access control and input handling, see our web application security guide.

Most mature web frameworks include CSRF protection. OWASP's first recommendation is to check for it and turn it on before building anything custom. The common failures are not missing libraries. They are endpoints excluded from protection "temporarily," API routes added outside the protected middleware, and validation that only runs when a token is present.
The server generates a secret, unpredictable token, stores it in the user's session and embeds it in each form or page. Every state-changing request must return it in a hidden field or a custom header, and the server rejects any request where it is missing or does not match. OWASP's rules for tokens:
Per-request tokens are stricter but can break the browser's Back button. Per-session tokens are the usual compromise.
When the server keeps no session state, the double-submit pattern sets a token in a cookie and requires the same value in a header or form field. OWASP now documents the naive version, a random value with no signature, "for reference only." Its weakness is that an attacker who can write cookies for your domain, for example from a sibling subdomain, can plant a matching pair. The recommended version is signed: an HMAC over a session-bound value with a server-side secret key, compared in constant time and sent with the Secure attribute.
Do not rely on browser defaults. Set the attribute on every session cookie:
Set-Cookie: __Host-session=<session-id>; Path=/; Secure; HttpOnly; SameSite=LaxUse Strict where the user experience allows it, and Lax where users must arrive logged in from links on other sites. The __Host- prefix forces the cookie to be host-only and Secure with Path=/, which stops subdomains from overwriting it. Treat all of this as defense in depth, not a replacement for steps 2 and 3.
Browsers send an Origin header on cross-origin requests and on POST requests. The server compares it with its own origin and rejects mismatches. If Origin is missing, OWASP suggests checking the hostname in Referer. Three rules matter:
app.example.com.attacker.example through.Origin: null as proof of a same-origin request.Host header.Modern browsers send a Sec-Fetch-Site header on every request with one of four values: same-origin, same-site, cross-site or none (a user action such as typing the URL or opening a bookmark). According to MDN's compatibility data, Chrome has sent it since version 76, Firefox since version 90 and Safari since version 16.4. Google's web.dev guide to Fetch Metadata calls the resulting check a Resource Isolation Policy.
OWASP's recommended policy is to reject unsafe methods (POST, PUT, PATCH, DELETE) when the value is cross-site, allow same-origin, and treat same-site carefully unless you trust every subdomain. Because older clients may not send the header, a fallback such as the Origin check is mandatory. Run the policy in log-only mode first, and include the Sec-Fetch headers in Vary if responses are cached. The full header set is defined in the W3C Fetch Metadata Request Headers specification.
The sketch below is illustrative, not a drop-in library. It shows how the checks fit together in a Python web app:
# Illustrative sketch: layered CSRF check for state-changing requests.
import hmac
TRUSTED_ORIGIN = "https://app.example.com" # configured, not taken from Host
UNSAFE = {"POST", "PUT", "PATCH", "DELETE"}
def csrf_allowed(method, headers, session_token, submitted_token):
if method not in UNSAFE:
return True # GET must never change state
site = headers.get("Sec-Fetch-Site")
if site == "cross-site":
return False # Fetch Metadata: block cross-site writes
if site is None: # older client: fall back to Origin
origin = headers.get("Origin")
if origin is None or origin == "null" or origin != TRUSTED_ORIGIN:
return False
# Token check still runs for every unsafe request.
if not session_token or not submitted_token:
return False
return hmac.compare_digest(session_token, submitted_token)For JSON APIs, require Content-Type: application/json and reject everything else, including text/plain. Or require a custom header that a cross-site page cannot send without a preflight. Keep the CORS allowlist to exact origins you control and never combine a reflected origin with credentials. Prefer bearer tokens in the Authorization header over cookies for APIs called by first-party JavaScript, where that fits your architecture.
Never change state on GET, HEAD or OPTIONS. For high-impact actions such as changing an email, password, MFA setting or payout account, require re-authentication or a one-time code. That limits the damage of an endpoint someone forgot to protect. OWASP specifically advises against relying on a CAPTCHA as a CSRF defense.
OWASP's cheat sheet puts it bluntly: cross-site scripting can defeat all CSRF mitigation techniques. Script running in your origin can read the token from the page and send requests that are same-origin by definition. CSRF defenses only hold if your XSS defenses do too. Our cross-site scripting (XSS) guide covers those.
| Control | What good looks like | How to verify |
|---|---|---|
| Framework protection | Enabled globally, with no unreviewed exemptions | Review middleware configuration and exclusion lists |
| Token validation | Every unsafe request requires a valid, session-bound token | Remove or swap the token in a lab request; expect rejection |
| Double-submit (if used) | Signed with HMAC and bound to the session | Confirm a token from another session fails |
| Session cookie | Secure, HttpOnly, explicit SameSite, __Host- prefix where possible | Inspect Set-Cookie responses |
| Origin check | Exact match against a configured origin; missing or null rejected | Send lab requests with a foreign, null or missing Origin |
| Fetch Metadata | Cross-site unsafe requests rejected, with a fallback | Review logs from log-only mode before enforcing |
| Safe methods | No state change on GET or method-override paths | Map routes and test GET and override variants |
| JSON APIs | Strict Content-Type or custom header required | Send text/plain and form-encoded variants in a lab |
| CORS | Exact origin allowlist; no reflected origin with credentials | Review CORS configuration and responses |
| Sensitive actions | Re-authentication or one-time code | Walk through email, password, MFA and payout changes |
| Login and OAuth | Pre-session login token; state, PKCE or nonce verified | Test login and callback flows in a lab |
Where these checks belong in the development lifecycle, from design review to pre-release testing, is covered in our secure SDLC guide.
CSRF testing is low-risk compared with many web tests, but it still changes data by definition. Professional testing follows a few rules: written authorization, test accounts the tester owns, and forged requests aimed only at a lab or staging environment, never at real users' sessions. The OWASP Web Security Testing Guide section on CSRF is the standard reference. The workflow below is a method, not a payload list.
Testers list each request that changes something: profile, email, password, MFA, roles, payments, integrations, API keys, admin settings, and the login and logout flows. For each one they record the HTTP method, the content type, how the user is authenticated (cookie, header token or both) and any token parameter.
The tester checks each session cookie's SameSite, Secure and HttpOnly attributes and whether it uses a __Host- prefix. A cookie with no SameSite attribute is a signal to test in more than one browser engine. If authentication uses only an Authorization header that the app's JavaScript sets, classic CSRF is unlikely, and the effort moves to how that token is stored.
Using their own test account, testers replay a legitimate request with one change at a time and record whether the server still accepts it:
text/plain bodyOrigin header changed to a foreign site, set to null or removedAny accepted variant is a candidate finding. DeepStrike's client-side vulnerabilities write-up describes one such case, where the server issued CSRF tokens but did not reject requests with a missing or altered token.
A candidate becomes a finding when a request from a different site really works. The tester hosts a minimal test page on a domain they control, like the example form earlier, and opens it in a browser signed in to the lab app with a test account. Because defaults differ, this step is repeated in a Chromium-based browser and in Firefox or Safari. A result such as "exploitable in Firefox and Safari, blocked by Lax-by-default in Chrome" is still a valid finding, with severity adjusted for the user base.
Testers check whether the login form accepts a cross-site submission, whether OAuth callbacks verify state (or PKCE or a nonce), and whether CORS reflects arbitrary origins with credentials. That last issue can turn a write-only CSRF into a full read of the victim's data.
A useful CSRF finding names the endpoint and action, shows which checks failed, states the browsers in which it was reproduced, and explains the realistic impact. A forged email change that enables account takeover is rated very differently from a forged newsletter preference. After the fix, the same lab test is repeated to confirm rejection.
The three names sound alike, and all three abuse trust, but they abuse different trust in different places.
| CSRF | XSS | SSRF | |
|---|---|---|---|
| Who sends the malicious request | The victim's browser | The victim's browser, running attacker script inside your site | Your server |
| What trust is abused | The server's trust in the user's cookies | The browser's trust in your site's code | Internal systems' trust in your server |
| Can the attacker read responses | No, not without a CORS mistake | Yes, the script runs in your origin | Sometimes; blind SSRF returns nothing |
| Typical impact | One unwanted action per request, such as an email change | Full control of the user's session in the page | Access to internal services and cloud metadata |
| Main fixes | Tokens, origin and Fetch Metadata checks, SameSite cookies | Output encoding, safe DOM APIs, CSP | Destination allowlists, egress controls, IMDSv2 |
| CWE | CWE-352 | CWE-79 | CWE-918 |
They also chain. XSS defeats CSRF defenses because script in your origin can read tokens. CSRF does not run script by itself, but it can submit a value that a separate stored-XSS bug later renders. SSRF is the server-side mirror image of CSRF: in SSRF the forged request comes from your server, as explained in our guide to server-side request forgery.
These three are among the most common web vulnerabilities we see described in public research, and access-control bugs such as insecure direct object references (IDOR) often sit next to them in the same features.
CSRF is when a website you visit secretly makes your browser send a request to another site where you are logged in. Your browser adds your login cookie automatically, so the other site thinks you asked for the action, such as changing your email or password.
A CSRF token is a secret, unpredictable value the server puts in each form or page and requires back with every state-changing request. A malicious site cannot read or guess it, so forged requests arrive without a valid token and the server rejects them.
It blocks the most common form, a cross-site POST, but it is not a complete defense. Lax still allows cookies on cross-site GET navigations, treats subdomains as the same site, and is only the default in Chromium-based browsers. Firefox and Safari do not apply it to cookies that lack the attribute. Use it with tokens or origin checks.
CSRF makes the victim's browser send a request from another site and cannot read the response. XSS runs attacker script inside your site, so it can read pages and act freely as the user. XSS can bypass CSRF defenses by reading the token.
They can be. An API that strictly requires application/json or a custom header, or that authenticates with a bearer token in the Authorization header, resists classic CSRF. One that also accepts form or text/plain bodies, or has a CORS policy that trusts any origin with credentials, is vulnerable.
That error usually means the protection is working and the request did not include the token. Make sure the form or JavaScript sends the token the framework issued, in the field or header it expects, and that the session cookie is being sent. Do not fix it by disabling CSRF protection on the endpoint.
Yes, as part of a broader category. In the OWASP Top 10:2025, CWE-352 Cross-Site Request Forgery is listed under A01:2025 Broken Access Control.
CSRF looks solved until you check. Browser defaults protect some users and not others, new API routes skip middleware, and a single permissive CORS rule can undo every token. The checks that matter are concrete: which actions accept a request without a valid token, which cookies lack an explicit SameSite attribute, and which browsers your users actually run.
DeepStrike's web application penetration testing services combine automated discovery with rigorous manual testing aligned to the OWASP Top 10. Only vulnerabilities validated through exploitation are reported, each with actionable remediation steps. To scope a test, contact DeepStrike.
Mohammed Khalil is a Cybersecurity Architect at DeepStrike, specializing in advanced penetration testing and offensive security operations. With certifications including CISSP, OSCP, and OSWE, he has led numerous red team engagements for Fortune 500 companies, focusing on cloud security, application vulnerabilities, and adversary emulation. His work involves dissecting complex attack chains and developing resilient defense strategies for clients in the finance, healthcare, and technology sectors.
MITRE CWE-352: Cross-Site Request Forgery (CSRF)
OWASP Top 10:2025 A01 Broken Access Control
OWASP Cross-Site Request Forgery Prevention Cheat Sheet
OWASP Web Security Testing Guide: Testing for Cross Site Request Forgery
MDN: Cross-site request forgery (CSRF)
MDN: Set-Cookie header reference
MDN browser-compat-data (GitHub)
web.dev: Protect your resources from web attacks with Fetch Metadata
W3C: Fetch Metadata Request Headers
Barth, Jackson and Mitchell: Robust Defenses for Cross-Site Request Forgery (ACM CCS 2008)
RFC 6749: The OAuth 2.0 Authorization Framework
RFC 9700: Best Current Practice for OAuth 2.0 Security (January 2025)

Stay secure with DeepStrike penetration testing services. Reach out for a quote or customized technical proposal today
Contact Us