Guide
CSRF and SSRF explained
Cross-Site Request Forgery (CSRF) and Server-Side Request Forgery (SSRF) both abuse trust in HTTP requests — but from opposite directions. CSRF makes a victim's browser send an authenticated request the victim never intended. SSRF makes your server fetch a URL an attacker chose, often reaching internal networks, cloud metadata endpoints, or admin panels that are invisible from the public internet. Neither attack needs stolen passwords if your session cookies or server-side fetch logic are too permissive. This guide explains how each attack works, how they differ from SQL injection and XSS, and the layered defenses — SameSite cookies, synchronizer tokens, URL allowlists, and network segmentation — that belong in every production checklist alongside OAuth and JWT session design.
CSRF: when the browser betrays the user
Browsers automatically attach cookies to requests matching the cookie's domain.
That convenience is what makes login sessions work — but it also means any site
can trigger a request to bank.example.com and the browser will
include the victim's session cookie if they are logged in. CSRF exploits this
automatic credential attachment.
The classic attack flow:
- Alice logs into
bank.example.com. Her session cookie is stored in the browser. - Alice visits
evil.examplewhile still logged in. - Evil's page contains a hidden form or image tag that POSTs to
bank.example.com/transfer?to=attacker&amount=1000. - The browser sends the request with Alice's cookie. The bank sees a valid session and processes the transfer.
Alice never clicked "Transfer" — she only visited a malicious page. The attack succeeds because the bank trusted the cookie without verifying that Alice intentionally initiated the action from the bank's own origin.
Why CSRF still matters in SPA and API eras
Modern single-page apps often use JSON APIs with
Authorization: Bearer headers instead of cookie sessions. Bearer
tokens in localStorage are not automatically sent cross-origin, so
classic CSRF against those APIs is harder. But many apps still use
cookie-based sessions (especially with server-rendered pages,
admin panels, and legacy endpoints). Payment processors, email providers, and
settings forms frequently remain cookie-authenticated. Any state-changing
endpoint that accepts cookies without additional proof of intent is a CSRF
candidate.
GET requests must never change state. A CSRF attack via
<img src="https://bank.example.com/transfer?..."> only works
if the transfer endpoint accepts GET. Use POST, PUT, PATCH, or DELETE for
mutations — but that alone is not a defense; attackers can forge POST forms too.
CSRF defenses that actually work
No single control is sufficient. Production apps layer multiple mitigations:
SameSite cookies
Set session cookies with SameSite=Lax (default in modern browsers)
or SameSite=Strict. Lax blocks cookies on cross-site
POST requests (the CSRF sweet spot) while still allowing top-level navigation
GETs. Strict blocks cookies on all cross-site requests, which can
break legitimate flows like OAuth redirects — test before deploying.
SameSite=None; Secure is required for cross-site embedded contexts
(iframes, third-party widgets) and re-opens CSRF unless you add tokens.
Synchronizer tokens (CSRF tokens)
The server generates a random, unguessable token per session (or per form) and
embeds it in pages as a hidden field or meta tag. State-changing requests must
include the token in the body or a custom header
(X-CSRF-Token). Attackers on other origins cannot read the token
due to the same-origin policy, so forged requests fail validation. Frameworks
like Django, Rails, Laravel, and Spring ship built-in synchronizer token support
— use it instead of rolling your own crypto.
Double-submit cookie pattern
A lighter alternative: set a random value in a non-HttpOnly cookie
and require the same value in a request header or field. The attacker's origin
cannot read your domain's cookies to copy the value into a forged request.
Weaker than synchronizer tokens if subdomains can set cookies on the parent
domain — scope cookies carefully.
Custom headers and Content-Type checks
Browsers restrict cross-origin requests that set custom headers (like
X-Requested-With or Authorization) unless CORS
preflight allows them. Requiring a custom header on JSON API mutations blocks
simple HTML form CSRF. Similarly, requiring
Content-Type: application/json blocks standard form submissions.
These are useful supplements, not replacements — CORS misconfiguration can
undermine them.
Re-authentication for sensitive actions
Require password, WebAuthn, or OTP confirmation before irreversible operations: changing email, adding payout addresses, disabling 2FA, or transferring funds. Even a perfect CSRF token can be bypassed if an XSS flaw exists on your origin — step-up auth limits blast radius.
SSRF: when your server fetches attacker-chosen URLs
SSRF flips the trust model. Instead of abusing the victim's browser, the attacker
supplies a URL (or hostname) that your backend will fetch: image
proxies, PDF renderers, webhook validators, "import from URL" features, RSS
readers, and LLM tool calls that browse the web. If your server requests
http://169.254.169.254/latest/meta-data/ (AWS instance metadata)
or http://127.0.0.1:6379/ (local Redis), the attacker reads
responses your firewall intended to hide.
Common SSRF targets
- Cloud metadata — AWS, GCP, and Azure metadata endpoints expose IAM credentials, instance identity, and internal network hints.
- Loopback services — Admin panels, databases, Redis, Elasticsearch, and message queues bound to
127.0.0.1. - Internal RFC 1918 networks —
10.x,172.16–31.x,192.168.xhosts not routed from the public internet. - Link-local and special ranges —
169.254.x(link-local),0.0.0.0, IPv6::1. - Protocol smuggling —
file://,gopher://,dict://handlers in misconfigured HTTP clients.
A 2023 SSRF against a major CI vendor leaked signing keys by reaching an internal metadata service through a user-controlled "fetch build artifact URL" field. The pattern repeats wherever servers act as HTTP clients on behalf of users.
Blind vs non-blind SSRF
Non-blind SSRF returns the fetched response to the attacker (image preview, URL import result). Blind SSRF triggers a request but does not return the body — attackers infer success via timing, DNS callbacks to their domain, or side effects (e.g., an email sent from an internal relay). Blind SSRF is harder to detect in logs but still dangerous for port scanning and triggering internal actions.
SSRF defenses: assume every URL is hostile
Allowlists, not blocklists
Blocklists fail. Attackers encode IPs as decimals (2130706433 for
127.0.0.1), use DNS rebinding, redirect chains, IPv6 mappings, and alternate
hostnames (localtest.me resolves to 127.0.0.1). If your feature
only needs to fetch public HTTPS images, allowlist specific domains — not "block
localhost." Parse URLs with a hardened library, resolve DNS yourself, and
validate the resolved IP against your allowlist after resolution to
defeat rebinding.
Disable redirects or re-validate each hop
An allowlisted URL can 302 redirect to http://169.254.169.254/.
Either disable automatic redirect following or run the full allowlist check on
every redirect target before fetching.
Network segmentation and egress controls
Run URL-fetch workers in an isolated subnet with no route to metadata IPs or internal VLANs. Use cloud provider features (IMDSv2 on AWS requires a PUT token before metadata reads) as defense in depth, not primary protection. Egress firewalls that deny RFC 1918 and link-local destinations from app tiers catch SSRF that application code misses.
Minimal response exposure
Do not echo raw fetched bytes to users. Stream images through a sanitizer; return only success/failure for webhook pings. Rate-limit and log outbound fetches per user. Time out aggressively — SSRF port scans often show up as bursts of short-timeout requests to sequential ports.
Protocol and port restrictions
Restrict client libraries to https:// on ports 443 (and 80 only if
unavoidable). Disable file://, FTP, and obscure schemes. Use
dedicated fetch libraries (not shell curl wrappers) with explicit
scheme handlers.
CSRF vs SSRF: same acronym shape, different threat
| Dimension | CSRF | SSRF |
|---|---|---|
| Who sends the request? | Victim's browser (with their cookies) | Your server (with its network position) |
| Attacker goal | Perform actions as the logged-in user | Reach internal resources or steal cloud credentials |
| Primary trust broken | Server trusts cookie without proof of intent | Server trusts user-supplied URLs for outbound fetch |
| Key defenses | SameSite cookies, CSRF tokens, step-up auth | URL allowlists, DNS re-validation, egress firewalls |
| Often paired with | XSS (steals tokens if present on page) | Open redirects, XXE, PDF generators |
Both attacks sit in the OWASP Top 10 family of "broken access control" and "security misconfiguration." A wallet or fintech app might face CSRF on cookie-authenticated payout settings and SSRF on a "verify receipt from URL" import — audit both surfaces independently.
Production checklist
CSRF
- Session cookies use
SameSite=LaxorStrictplusSecureandHttpOnly. - All state-changing cookie-authenticated endpoints validate synchronizer tokens or equivalent.
- GET endpoints are read-only; mutations use POST/PUT/PATCH/DELETE.
- Sensitive operations require step-up authentication regardless of CSRF tokens.
- CORS policy does not reflect arbitrary
Originheaders with credentials. - CSRF regression tests cover forms and JSON endpoints that accept cookies.
SSRF
- Every "fetch URL on behalf of user" code path uses domain allowlists and post-DNS IP validation.
- Redirects are disabled or re-validated at each hop.
- Outbound workers cannot reach metadata endpoints, loopback, or RFC 1918 ranges.
- Only HTTPS on standard ports; exotic URL schemes disabled in HTTP client config.
- Fetch timeouts, size limits, and per-user rate limits are enforced.
- Responses are not echoed raw; errors do not leak internal hostnames or stack traces.
Key takeaways
- CSRF weaponizes automatic cookie attachment — the victim's browser sends authenticated requests the victim did not intend.
- SSRF weaponizes your server's network position — attackers reach internal services through features that fetch user-supplied URLs.
- SameSite cookies and synchronizer tokens are the CSRF baseline; step-up auth protects high-impact actions.
- Allowlists and post-resolution IP checks beat blocklists for SSRF; segment egress at the network layer too.
- XSS defeats CSRF tokens on the same origin — fix injection bugs per the SQLi/XSS guide, and use CSP as a safety net.
- Audit both attack surfaces wherever cookies or server-side HTTP clients touch user input.
Related reading
- SQL injection and XSS explained — injection attacks that can steal CSRF tokens from the same origin
- Content Security Policy explained — limiting script execution when XSS slips through
- CORS explained — how cross-origin policies interact with CSRF and API design
- OAuth 2.0 and OpenID Connect explained —
stateparameter as CSRF protection in authorization flows