Cross-Site Request Forgery (CSRF): The Complete Practical Guide
Bypassing Referer-Based CSRF Defenses
How Referer-header CSRF checks are defeated: stripping the header, HTTPS-to-HTTP downgrade, naive prefix and substring matching, and empty-Referer allowances.
Published
Some applications try to stop CSRF by checking the Referer header instead of (or on top of) a token, reasoning that a forged request from an attacker's page will carry the attacker's Referer. The problem is that Referer is browser-set, advisory, strippable, and usually matched sloppily. This page covers defeating a Referer-based check. Practice on a free live target.
Step 1: confirm the app relies on Referer
First establish that the Referer is actually the gate. Take a state-changing request and alter the Referer to an unrelated value: if the request is now rejected, the app is validating it. Then remove the Referer entirely and see what happens. The two outcomes (rejected-when-wrong, and what happens when absent) tell you which bypass applies.
Why Referer is a weak control
The Referer header is set by the browser as a courtesy and was never meant to be a security boundary. Three facts make it unreliable as a defense:
- It can be suppressed by the referring page.
- It can be dropped on protocol downgrade.
- It is often matched naively, so an attacker URL can satisfy the check.
Each is a bypass.
Strip the header entirely
If the app allows the request when Referer is absent (common, because stripping Referer for privacy is legitimate), you just make your page send no Referer. A meta tag or a response header on the attacker page does it:
<meta name="referrer" content="no-referrer">Referrer-Policy: no-referrerServed from your attacker page, either one means the forged request arrives with no Referer at all, and an "empty is allowed" check waves it through.
Downgrade to drop it
On some referrer policies, a navigation from HTTPS to HTTP drops the Referer. If the target accepts the request without a Referer and can be reached over a downgraded path, the downgrade itself strips the header without you setting a policy.
Fool a naive match
Many Referer checks do a substring or prefix comparison instead of parsing the URL and checking the host properly. Both are defeatable with an attacker-controlled URL that contains the expected string:
Prefix/suffix flaw: https://target.example.evil.example/attack
(host is evil.example, but a check for "target.example" as a prefix of the host passes)
Substring flaw: https://evil.example/?x=https://target.example/
(a check for "contains target.example" passes because it appears in the query string)In the first, the real registrable domain is evil.example, and the expected string just appears as a label. In the second, the expected origin appears in a query parameter you control. A check that does not parse the URL and compare the actual host is defeated by putting the magic string somewhere harmless.
One modern caveat on the query-string variant: browsers now default to the strict-origin-when-cross-origin referrer policy, which trims a cross-origin Referer down to just the origin (no path or query). So the magic string hidden in a query parameter is stripped before the server sees it, unless your attacker page forces the full URL with Referrer-Policy: unsafe-url. The host-based suffix variant needs no such trick, because the magic string lives in the origin itself, which is always sent.
Step through it
Confirm Referer is the gate, pick the matching bypass (suppress it where empty is allowed, downgrade to drop it, or craft a URL that satisfies the naive match), and replay the CSRF request with that Referer. The confirmation is the state change firing despite the "protection."
Fixing it
- Do not rely on Referer as the primary CSRF defense. Use a session-bound synchronizer token. Referer or Origin checking is defense-in-depth only.
- Prefer the
Originheader to Referer when you do header-check, since it is harder to spoof and does not leak the path. - Fail closed on a missing header if you check it, rather than allowing the request when it is absent.
- Parse and compare the actual host, never a substring or prefix of the raw header.
Full guidance is in preventing CSRF. Next, see the technique that beats even a perfect token in CSRF via XSS, or try a Referer bypass on a free lab.
← Back to Cross-Site Request Forgery (CSRF): The Complete Practical Guide