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:

  1. Alice logs into bank.example.com. Her session cookie is stored in the browser.
  2. Alice visits evil.example while still logged in.
  3. Evil's page contains a hidden form or image tag that POSTs to bank.example.com/transfer?to=attacker&amount=1000.
  4. 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 networks10.x, 172.16–31.x, 192.168.x hosts not routed from the public internet.
  • Link-local and special ranges169.254.x (link-local), 0.0.0.0, IPv6 ::1.
  • Protocol smugglingfile://, 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=Lax or Strict plus Secure and HttpOnly.
  • 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 Origin headers 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