Glossary

SSRF

SSRF is the attack class where an outside user tricks a server into making requests on their behalf, usually to internal infrastructure that's unreachable from the public internet. Any service that fetches a URL provided by user input (including most CORS proxies, webhook validators, and link-preview generators) is a potential SSRF surface.

Why it's dangerous

The attacker's machine can't reach 169.254.169.254 (the AWS metadata service) or 10.0.0.5 (an internal Redis without auth). The server can. If the server will fetch any URL it's given, the attacker just asks it to fetch those internal URLs and returns the result. That's how cloud breaches happen.

Classic targets

How a CORS proxy gets weaponized

// Innocent CORS proxy code (DO NOT DEPLOY)
app.get('/proxy', async (req, res) => {
  const target = req.query.url;
  const upstream = await fetch(target);
  res.set('Access-Control-Allow-Origin', '*');
  res.send(await upstream.text());
});

// Attacker:
GET /proxy?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
// Server happily fetches AWS creds and returns them.

The standard mitigations

  1. Resolve hostnames and check the IP against an allowlist or denylist before fetching. The check must happen on the resolved IP, not the hostname: localtest.me resolves to 127.0.0.1. Re-check every redirect hop, and if the check is a separate lookup before the fetch, remember that DNS rebinding can race it.
  2. Reject private and reserved ranges: 0.0.0.0/8, 10/8, 100.64.0.0/10 (carrier-grade NAT), 127.0.0.0/8, 169.254.0.0/16, 172.16/12, 192.168/16, ::1, fc00::/7, fe80::/10.
  3. Reject non-HTTP schemes like file://, gopher://, data://, which open additional protocol-confusion attacks.
  4. Strip dangerous request headers: never forward incoming Authorization, Cookie, or proxy-control headers to the upstream.
  5. Limit response size and time so the proxy can't be used as a relay for terabyte exfiltration.
  6. Rate-limit per account: probing usually takes many requests, and per-key limits follow an attacker who rotates IP addresses.

corsproxy.dev refuses literal private, loopback, link-local and cloud-metadata targets, and hostnames that its DNS check resolves to them, with a 403 BLOCKED_HOST error. For the details and limits of that check, see the security writeup.

See also

CORS proxy CORS Blog · CORS proxy security