---
name: csrf-detect
description: Finding Cross-Site Request Forgery (CSRF) vulnerabilities in web applications by mapping state-changing actions, checking browser-automatic credential use, evaluating CSRF tokens, SameSite cookies, Origin/Referer defenses, and building safe browser-based proof-of-concept requests for authorized testing.
---

# Overview

This skill focuses on detecting Cross-Site Request Forgery (CSRF) in authorized web application assessments. CSRF exists when a logged-in user's browser can be induced by another site to send a state-changing request that the user did not intend, and the target server accepts the request because browser-held credentials such as cookies or HTTP authentication are automatically attached. 

The goal is to prove, safely and non-destructively, whether a cross-origin page can cause a meaningful state change in a test account without knowing the victim's credentials or reading the target response. CSRF is primarily an unwanted-action vulnerability; response reading normally requires a separate issue such as CORS misconfiguration or XSS. 

### Prerequisites & Tools

- **Playwright MCP**: Use a real browser session to log in as a victim test account, visit a proof-of-concept page from another origin, observe network requests, and confirm before/after state changes. 
- **Burp MCP / Burp Suite**: Capture baseline requests, compare tokens and cookies across sessions, modify method/content type/token fields, and generate browser-safe CSRF proof-of-concept HTML from a captured request. 
- **Local static PoC page**: Host simple HTML forms, links, images, or scripts from an origin different from the target to validate browser behavior. 
- **Optional tooling**: XSRFProbe can help enumerate common CSRF cases, but manual browser verification is required before reporting. 

# Mindset

- **RULE: CSRF = ambient authority + state change.** Browsers automatically attach cookies and HTTP auth to eligible requests; a vulnerability exists when the server treats that ambient authority as sufficient proof of user intent. 

- **A vulnerable CSRF target usually has three properties:** a sensitive action to force, cookie-based or browser-managed authentication, and no unpredictable request value that the attacker cannot supply. 

- **CSRF is usually one-way.** The attacker can cause an action but normally cannot read the response. If CORS or XSS allows response reading, impact may expand beyond CSRF. 

- **Do not trust method names.** POST is forgeable with forms, GET is especially dangerous for state changes, and JSON endpoints may still be reachable through `text/plain`, form encoding, parser confusion, method override, or alternate routes. 

- **Defenses fail in implementation details.** Tokens may be missing, optional, method-dependent, reusable across sessions, duplicated in cookies, or not bound to the current user/session/action. Origin/Referer checks may fail open, and SameSite protections are often misunderstood. 

- **Same-site is not same-origin.** SameSite cookie behavior is based on scheme plus registrable domain, while origin also includes exact host and port. Sibling subdomains can be cross-origin but same-site, which matters for CSRF and for XSS on sibling domains. 

- **CORS is not a CSRF defense by itself.** CORS mainly controls whether a script can read a response. HTML forms, top-level navigations, and other simple requests can still send browser credentials. 

- **XSS can undermine CSRF defenses.** XSS can often read tokens or issue same-origin requests, while CSRF tokens only protect the specific requests where they are enforced. Treat XSS as higher-impact and consider it when evaluating CSRF residual risk. 

- **Stay safe and evidence-based.** Use test accounts, reversible changes, small benign values, and before/after screenshots or API reads. Do not perform destructive actions, mass actions, token brute force, or tests outside the authorized scope. 

# Approach

## Classification

Classify each suspected issue by the defense or request property that fails:

- **No CSRF defense:** a cross-origin link, image, top-level navigation, or form causes the authenticated state change. 
- **Token validation weakness:** token omitted, blank, invalid, method-dependent, only checked when present, reusable across sessions, tied to a non-session cookie, or duplicated in a cookie. 
- **SameSite bypass or misconfiguration:** state-changing GET under Lax, missing SameSite, newly issued cookie grace window, sibling-domain same-site abuse, or on-site gadget. 
- **Origin/Referer weakness:** validation skipped when header is absent, naive string parsing, weak starts-with/contains checks, or unsafe handling of unusual/null origins. 
- **Content-type or parser confusion:** JSON-like actions accepted through `text/plain`, `application/x-www-form-urlencoded`, `multipart/form-data`, duplicate keys, or array-style parameters. 
- **Method override / unsafe GET:** `_method`, `X-HTTP-Method-Override`, route tunneling, or framework behavior turns a simple request into a mutating server-side action. 
- **Modern API surfaces:** REST actions, GraphQL mutations or persisted queries, WebSocket handshakes relying on cookies, OAuth/OIDC connect/disconnect/logout, and admin/back-office workflows. 

## Attack Surface Mapping

Prioritize endpoints that change user, financial, security, or authorization state:

- Account profile fields, email, password, username, avatar, notification settings, and account deletion. 
- MFA enable/disable, backup codes, recovery email/phone, session logout, OAuth/OIDC link/unlink, and SSO flows. 
- Role changes, invitations, staff/admin actions, impersonation, access-control changes, and tenant/project membership. 
- Payments, subscriptions, transfers, orders, bids, billing addresses, shipping addresses, and saved payment preferences. 
- API keys, personal access tokens, SSH keys, webhooks, integrations, and key rotation. 
- File upload/delete, content publishing, messages, social actions, and integration-side effects. 
- Login CSRF, logout chains, stored CSRF through user-generated HTML, and flows where HTML injection can persist a request trigger. 
- GraphQL mutations, persisted queries via GET, WebSocket handshakes, and endpoints reachable from intranet/admin browsers. 

For every candidate, record method, URL, parameters, body format, content type, cookies, custom headers, token fields, Origin/Referer handling, redirects, and visible state changes. 

# Methodology

## Step 1: Establish Scope, Safety, and Test Accounts

**Objective:** avoid harmful impact while preserving evidence quality.

- Confirm the target, accounts, roles, and actions are authorized for testing. 
- Use at least two test accounts when possible: one victim account and one separate account for token/session comparison. 
- Select reversible, benign state changes such as display name, test avatar, notification flag, or a lab-only setting. Avoid destructive operations unless explicitly authorized and safely restorable. 
- Capture the initial state with screenshots, API reads, or UI observations before testing. 

## Step 2: Inventory State-Changing Requests

**Objective:** build a map of endpoints that may be CSRF-relevant.

- Browse the application as the victim account and use Burp, browser devtools, or Playwright network logs to record all requests while changing settings and using high-value workflows. 
- Include hidden/admin/back-office areas, REST endpoints, GraphQL operations, file actions, WebSocket handshakes, OAuth flows, logout, login, and invitation/role-management paths. 
- Flag any state-changing GET request immediately because it may work through a link, image, redirect, or top-level navigation. 
- Note each request's method, parameters, body type, content type, cookies, anti-CSRF token, custom headers, and visible state effect. 

## Step 3: Check CSRF Preconditions

**Objective:** determine whether CSRF is technically plausible.

- Confirm the request succeeds when authenticated and fails or has no effect when unauthenticated. 
- Identify whether authentication is browser-managed: session cookies, HTTP Basic/Digest/Negotiate, client certificates, or mixed cookie/bearer flows. 
- If the action only works with an `Authorization` header added by same-origin JavaScript and no browser-sent cookies or HTTP auth, classic CSRF is unlikely unless CORS, XSS, or another issue lets an attacker set the required header. 
- Identify attacker-unknown values: CSRF token, nonce, current password, re-auth challenge, one-time code, or required custom header. If a simple cross-site request cannot supply them, the endpoint may be protected. 

## Step 4: Baseline the Legitimate Request and Defenses

**Objective:** understand exactly what the server validates.

- Capture a clean legitimate request and response for the state change. 
- Locate CSRF tokens in hidden form fields, JSON bodies, headers, meta tags, cookies, or framework-specific parameters. 
- Inspect session cookies for `SameSite=Strict`, `Lax`, `None`, no attribute, `Secure`, and `HttpOnly`. Missing SameSite may receive browser-specific Lax-by-default behavior. 
- Note whether the request includes Origin or Referer and whether the server appears to enforce them. 
- Record any custom headers such as `X-CSRF-Token`, `X-Requested-With`, or API-specific headers; custom headers usually require CORS preflight for cross-site JavaScript, but forms cannot set them. 

## Step 5: Build a Basic Browser-Based CSRF PoC

**Objective:** prove whether a different origin can trigger the action.

- For GET actions, try a top-level link/navigation first, then image-style loading only if the endpoint does not require top-level navigation. 
- For POST form actions, create an HTML form with hidden fields and `application/x-www-form-urlencoded` or `multipart/form-data`, then auto-submit it from another origin. 
- Use Burp Suite's generated CSRF PoC as a starting point, but remove any cookies from the HTML; the victim browser must attach cookies naturally. 
- Open the PoC in a browser already authenticated as the victim and verify the network request, cookie attachment, and resulting state change. 
- Repeat the PoC from a logged-out browser or different unauthenticated context to confirm that the action depends on victim credentials. 

## Step 6: Test CSRF Token Validation Weaknesses

**Objective:** determine whether token-based defenses are actually enforced.

- **Token omitted:** remove the entire token parameter or header, not just its value. Vulnerable evidence: the action still succeeds. 
- **Blank or invalid token:** submit an empty or random benign value. Vulnerable evidence: the action succeeds with a token the server should reject. 
- **Method-dependent validation:** replay the same action using GET instead of POST, or another server-supported method. Vulnerable evidence: the state change succeeds when token validation is skipped for that method. 
- **Token only checked when present:** remove the token field entirely and compare with an invalid-token request. Vulnerable evidence: missing token is accepted while invalid token is rejected. 
- **Session binding:** obtain a token in account/session A and submit it with account/session B. Vulnerable evidence: token from a different session is accepted. 
- **Cookie-bound or duplicated token:** if a token value appears both in a cookie and request parameter, test whether an attacker-controlled matching pair is enough. Vulnerable evidence: an invented or attacker-set cookie+parameter pair succeeds. 
- Do not brute force token values; CSRF testing should prove validation logic flaws, not guess secrets. 

## Step 7: Test Simple Request and Parser Variants

**Objective:** find alternate encodings that avoid preflight or bypass server-side parsing assumptions.

- Try `application/x-www-form-urlencoded`, `multipart/form-data`, and `text/plain`; these are simple request content types that browsers can send cross-site with forms. 
- For JSON endpoints, check whether the server accepts JSON-shaped data submitted as `text/plain` or form data, or ignores the `Content-Type` header. 
- Test duplicate keys, array-style fields, nested parameter names, and parser-specific formats only with harmless values. Vulnerable evidence: an alternate parser path changes state without a valid CSRF defense. 
- Test method override parameters such as `_method=PUT`, `_method=DELETE`, or framework-specific route tunneling if the app uses them. Vulnerable evidence: a simple GET or POST form is treated as a protected mutating method. 
- If JavaScript `fetch` or XHR requires custom headers, browser preflight may block the request unless the target CORS policy allows it; do not count Repeater-only success as CSRF. 

## Step 8: Evaluate SameSite Cookie Behavior

**Objective:** determine whether cookie restrictions actually block the PoC context.

- Inspect every relevant cookie and distinguish explicit `SameSite=Strict`, explicit `Lax`, explicit `None`, and missing SameSite. 
- Test top-level cross-site GET navigation for endpoints that mutate state; Lax cookies may be sent in this context. 
- Check for state-changing GET plus method override, because a top-level GET can carry cookies and the server may internally perform a POST/PUT/DELETE action. 
- For cookies without explicit SameSite, consider browser Lax-by-default behavior and the short newly issued cookie window for top-level POSTs; verify carefully and document browser/version. 
- Test same-site but cross-origin sibling domains when in scope. A vulnerable sibling domain, redirect gadget, or XSS can cause requests that SameSite treats differently from truly cross-site requests. 
- Look for client-side on-site gadgets such as redirects or request generators that transform an external trigger into a same-site request. Server-side redirects do not always behave the same way. 

## Step 9: Evaluate Origin and Referer Defenses

**Objective:** identify fail-open or naive source validation.

- Compare requests with valid Origin/Referer, absent Referer, absent Origin where browser behavior allows it, and unusual origins such as sandboxed/null-origin contexts when safe. 
- Use a PoC page with `<meta name="referrer" content="no-referrer">` (or legacy `never` in older examples) to test whether the server skips validation when Referer is missing. Vulnerable evidence: the state change succeeds with no Referer. 
- Test naive parsing only in controlled domains you own or are authorized to use, such as `target.example.attacker.test` for starts-with checks or a URL containing the target hostname for contains checks. Vulnerable evidence: attacker-origin Referer is accepted. 
- If a defense requires the full Referer URL including query string, understand browser Referrer-Policy behavior and document any required policy such as `unsafe-url`. 
- Prefer robust Origin validation over Referer-only checks when recommending fixes, because Referer can be absent for privacy and policy reasons. 

## Step 10: Test Specialized Flows

**Objective:** cover high-impact CSRF variants that normal forms may miss.

- **Login CSRF:** determine whether an attacker can force a victim into an attacker-controlled account, creating account confusion or data leakage in later activity. 
- **Logout CSRF:** validate impact only if logout causes a security-relevant effect, such as forcing re-authentication into a malicious SSO flow. 
- **OAuth/OIDC CSRF:** test connect, unlink, authorize, callback, and logout flows for missing `state` validation or account-linking side effects. 
- **GraphQL:** test mutations and persisted queries, especially GET-based persisted queries or forms that can submit mutation variables through a simple request. 
- **WebSocket:** if the handshake relies on cookies, verify the server validates the `Origin` header before accepting cross-origin socket connections. 
- **Stored CSRF:** if user-generated HTML or rich text can persist tags such as benign image/link/form triggers, verify whether viewing the stored content causes an unintended state-changing request. 
- **Intranet/admin targets:** a victim's browser may reach internal apps that the tester cannot directly access from the public Internet; keep testing within explicit scope. 

## Step 11: Confirm, Reproduce, and Document Evidence

**Objective:** separate real CSRF from Repeater-only or same-origin-only behavior.

- A confirmed CSRF requires a browser visiting an attacker-origin page, no manual insertion of victim cookies, and an observable state change in the victim account. 
- Record the baseline request, PoC HTML, network evidence showing the cross-origin request, relevant cookies/SameSite attributes, Origin/Referer behavior, and before/after state. 
- Include a negative control such as logged-out PoC execution, invalid token rejection, or a properly protected endpoint for comparison. 
- Reproduce the issue at least twice and, where browser behavior matters, note browser/version and SameSite context. 
- Name the exact bypass trigger: missing token, token not bound to session, method switch, `text/plain` parser path, top-level GET under Lax, missing Referer accepted, sibling-domain same-site context, or other verified condition. 

# Examples

## Example 1: State-Changing GET via Top-Level Navigation

```
// Baseline authenticated request changes a harmless display name
Request GET /account/set-display-name?name=csrf-test HTTP/1.1
Cookie: session=<victim-session>

// Cross-origin PoC page
<a href="https://target.example/account/set-display-name?name=csrf-test">Continue</a>

// Vulnerability evidence
// 1. Victim is logged in.
// 2. Victim opens the attacker-origin page and follows/auto-navigates the link.
// 3. Browser sends victim cookies.
// 4. Display name changes to csrf-test.
```

Sources: GET/link/image CSRF and top-level navigation behavior. 

## Example 2: POST Form CSRF Without Token

```
// Baseline request captured while changing a benign setting
Request POST /profile/update HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Cookie: session=<victim-session>

nickname=csrf-test&newsletter=false

// Attacker-origin PoC; do not include cookies in the HTML
<form action="https://target.example/profile/update" method="POST">
  <input type="hidden" name="nickname" value="csrf-test">
  <input type="hidden" name="newsletter" value="false">
</form>
<script>document.forms.submit()</script>

// Vulnerability evidence: visiting the PoC while authenticated changes the profile.
```

Sources: auto-submitting forms and PortSwigger CSRF PoC workflow. 

## Example 3: Token Validation Depends on Presence

```
// Legitimate request contains a CSRF token
POST /settings/email
Content-Type: application/x-www-form-urlencoded
Cookie: session=<victim-session>

email=victim-test@example.com&csrf=validTokenFromPage

// Test A: invalid token should fail
email=victim-test2@example.com&csrf=invalid

// Test B: remove the entire token parameter
email=victim-test2@example.com

// Vulnerability evidence: Test B succeeds even though Test A fails.
```

Sources: token validation only when present or missing-token acceptance. 

## Example 4: Token Not Bound to Session

```
// Account A obtains tokenA from its settings page
// Account B submits tokenA with B's session cookie
POST /settings/display-name
Cookie: session=<accountB-session>
Content-Type: application/x-www-form-urlencoded

displayName=csrf-test&csrf=tokenA

// Vulnerability evidence: Account B's display name changes using Account A's token.
```

Sources: CSRF token not tied to user session. 

## Example 5: JSON Endpoint Accepts text/plain Form Submission

```
// Baseline JSON request
POST /api/profile
Content-Type: application/json
Cookie: session=<victim-session>

{"displayName":"csrf-test"}

// Test only if the server accepts or ignores text/plain
<form action="https://target.example/api/profile" method="POST" enctype="text/plain">
  <input name='{"displayName":"csrf-test","ignore":"' value='x"}'>
</form>
<script>document.forms.submit()</script>

// Resulting body is JSON-like text/plain. Vulnerable evidence: the API parses it and changes state.
```

Sources: JSON/text/plain form trick and simple request parser mismatch. 

## Example 6: Referer Validation Fails Open

```
// PoC suppresses Referer from the attacker-origin page
<meta name="referrer" content="no-referrer">
<form action="https://target.example/profile/update" method="POST">
  <input type="hidden" name="nickname" value="csrf-test">
</form>
<script>document.forms.submit()</script>

// Vulnerability evidence: request arrives with no Referer and the server still changes state.
```

Sources: Referer validation skipped when header is absent. 

# Tips and Tricks

## Advanced Techniques

- Build a **simple-request matrix** for each endpoint: GET navigation, form POST `application/x-www-form-urlencoded`, form POST `multipart/form-data`, and form POST `text/plain`. A Repeater request is not enough; validate with a browser. 
- Check **method override** parameters and framework route tunneling, especially when SameSite Lax allows top-level GET but the server honors `_method=POST` or `_method=DELETE`. 
- Test **token binding**, not just token presence. Strong tokens should be unique, secret, unpredictable, and tied to the user's session/action. 
- Treat **sibling subdomains** as important. SameSite may allow cookies across sibling domains, and XSS or redirect gadgets on one subdomain can affect another. 
- Look for **fresh-cookie flows** such as login, SSO, or OAuth that may create a short window for cookies without explicit SameSite on top-level POST requests. Document timing and browser version. 
- For **Referer defenses**, test absent header and naive string matching; do not rely on spoofed headers that browsers would not send from a real page. 
- For **WebSockets**, verify that cookie-authenticated handshakes reject untrusted Origin values before accepting actions on the socket. 
- If XSS is present anywhere in the same origin or relevant same-site context, reassess CSRF defenses because XSS can often obtain tokens or issue protected requests. 

## When to Skip

- Skip classic CSRF when the action requires only an `Authorization` header or custom header set by same-origin JavaScript, no cookies/HTTP auth are accepted, and CORS does not allow attacker origins to send the header. 
- Skip read-only/idempotent endpoints unless they trigger side effects such as sending email, changing server state, logging the user out with impact, or initiating a workflow. 
- Treat an endpoint as protected when every state-changing path enforces a valid token or equivalent proof of intent bound to the current session and action. 
- Skip SameSite-only bypass claims if cookies are not sent in the tested browser context and there is no same-site gadget, sibling-domain path, unsafe GET, or fresh-cookie condition. 
- Do not report Burp Repeater success as CSRF unless a real cross-origin browser PoC can trigger the change without manually adding victim secrets. 
- Do not continue testing an action if the only available proof would be destructive, irreversible, or outside scope; switch to a benign equivalent or ask for explicit authorization. 

## High-Value Targets

- Email, password, recovery factors, MFA settings, backup codes, session/logout behavior, and OAuth/OIDC account linking. 
- Role changes, invitations, admin/staff operations, impersonation, permission changes, and organization membership. 
- Payments, transfers, orders, subscriptions, billing data, bids, and stored payment preferences. 
- API keys, access tokens, SSH keys, webhooks, integrations, and rotation/revocation actions. 
- File upload/delete, profile images, content publishing, social actions, messages, and notification/email triggers. 
- Login flows and stored CSRF opportunities where user-controlled HTML can trigger actions for future viewers. 

## Best Practices for Remediation Guidance

- Use server-side anti-CSRF tokens that are unique, secret, unpredictable, and bound to the user's session and sensitive action. 
- Enforce token validation for every state-changing method and content type; reject missing, invalid, reused, or cross-session tokens. 
- Do not perform state changes via GET; require appropriate methods and strict content-type validation. 
- Validate `Origin` for state-changing requests where available, and use Referer as a fallback only with exact origin parsing and fail-closed behavior. 
- Set cookies deliberately with `SameSite=Lax` or `Strict` where compatible, `Secure`, and `HttpOnly`; do not rely on browser defaults alone. 
- Require re-authentication or step-up verification for highly sensitive changes such as password, MFA, payout, and admin actions. 
- Validate WebSocket `Origin` and apply CSRF-equivalent authorization checks to socket actions. 

## False Positives

- A request replayed in Burp with cookies proves authorization, not CSRF. The proof must come from a browser on a different origin. 
- A token visible in the victim's HTML is not attacker-known unless another vulnerability, such as XSS or information leakage, exposes it cross-origin. 
- `SameSite=None` or missing SameSite is not automatically a vulnerability; the action must be triggerable and cookies must be sent in the actual browser context. 
- Failed CORS preflight can make an XHR/fetch PoC invalid, but a form-based PoC may still work. Test both before deciding. 
- Redirects, 200 responses, or UI messages are insufficient by themselves; verify the final application state. 
- Browser privacy settings, extensions, and version differences can affect Referer and SameSite behavior; document the environment. 

## Confirmation Checklist

- A victim browser is authenticated before visiting the PoC. 
- The PoC is served from a different origin or authorized attacker-controlled context. 
- The PoC does not include victim cookies, passwords, current tokens, or manually copied secrets unless the test is specifically proving token reuse/binding. 
- The browser automatically sends credentials and the target accepts the request. 
- A visible, reversible state change occurs in the victim account. 
- A logged-out or unauthenticated control does not perform the same action. 
- The report names the exact failing defense and includes reproducible steps, request/response evidence, PoC, and before/after state. 

