• Agents
  • Pricing
  • Blog
Log in
Get started

Security for apps built with AI. Paste a URL, get a report, fix what matters.

Product

  • How it works
  • What we find
  • Pricing
  • Agents
  • MCP Server
  • CLI
  • GitHub Action

Resources

  • Guides
  • Blog
  • Docs
  • OWASP Top 10
  • Glossary
  • FAQ

Security

  • Supabase Security
  • Next.js Security
  • Lovable Security
  • Cursor Security
  • Bolt Security

Legal

  • Privacy Policy
  • Terms of Service
  • Cookie Policy
  • Imprint
© 2026 Flowpatrol. All rights reserved.
Home/OWASP Top 10/Web Top 10/A10: Server-Side Request Forgery (SSRF)
A10CWE-918

Make the server fetch a URL
Server-Side Request Forgery (SSRF)

The bug where a user-supplied URL turns your server into a proxy for the attacker.

A newer entry on the list, but one of the highest-impact when it lands.

Reference: Web Top 10 (2021) — A10·Last updated April 7, 2026·By Flowpatrol Team
Server-Side Request Forgery (SSRF) illustration

Your app has one innocent-looking feature. Paste a link, we fetch it, we show a preview. Your users love it. So do attackers, because the difference between 'fetch any URL' and 'fetch the cloud metadata endpoint and give me the keys' is zero characters of configuration.

Server-side request forgery is the bug where an attacker convinces your server to fetch a URL it should not fetch. From the attacker's side of the network, internal addresses like localhost, 10.0.0.0/8, and the cloud metadata service are unreachable. From your server's side they are one fetch call away. SSRF is the bridge between those two worlds.

What your AI actually built

You asked for a link-preview feature, or an avatar-from-URL uploader, or a webhook tester. The model wrote a handler that takes a URL from the request and calls fetch on it, then returns what it got back. Clean, short, works first try.

What it didn't write was the allowlist. The handler happily fetches http://169.254.169.254/latest/meta-data/ — the AWS instance metadata endpoint — which returns your IAM credentials to any caller inside the VM. Your server is inside the VM. The attacker is not, but your server is their proxy now.

The same shape applies to internal services. http://localhost:8080, http://10.0.0.7, http://redis:6379 — all of those are reachable from inside the cluster and none of them are reachable from the public internet. Until the attacker sends a URL and your server follows it.

How it gets exploited

Your app hosts a 'URL to PDF' feature. The attacker signs up and pastes a link.

Find out whether your server fetches URLs it should not.

Flowpatrol tests every URL-accepting input for SSRF, including cloud metadata endpoints. Five minutes. One URL.

Try it free
  • 1
    Probe the network
    They paste http://localhost:8080 and your server dutifully tries to fetch it, then returns the response body inside the PDF.
  • 2
    Find the metadata service
    They paste http://169.254.169.254/latest/meta-data/iam/security-credentials/ and receive the IAM role name attached to your VM.
  • 3
    Steal the credentials
    One more request to the role-name path returns a working set of temporary AWS access keys. Fully scoped, fully valid.
  • 4
    Cloud takeover
    With the keys they list S3 buckets, read the production database backup, and take a quiet copy. No password was ever guessed and no code was ever exploited.
  • Through a single 'paste a URL' feature the attacker walked away with full read access to your cloud account.

    Vulnerable vs Fixed

    Vulnerable — fetch any URL
    // app/api/preview/route.ts
    export async function POST(req) {
      const { url } = await req.json();
    
      const res = await fetch(url);
      const body = await res.text();
    
      return Response.json({ body });
    }
    Fixed — allowlist and block internal IPs
    // app/api/preview/route.ts
    import { lookup } from 'node:dns/promises';
    import { isIP } from 'node:net';
    
    const ALLOWED_PROTOCOLS = new Set(['http:', 'https:']);
    
    export async function POST(req) {
      const { url } = await req.json();
      const parsed = new URL(url);
    
      if (!ALLOWED_PROTOCOLS.has(parsed.protocol)) {
        return Response.json({ error: 'Bad scheme' }, { status: 400 });
      }
    
      const { address } = await lookup(parsed.hostname);
      if (isPrivateIp(address) || !isIP(address)) {
        return Response.json({ error: 'Blocked' }, { status: 400 });
      }
    
      const res = await fetch(parsed.toString(), { redirect: 'error' });
      const body = await res.text();
      return Response.json({ body });
    }

    Resolve the hostname yourself, reject private and link-local addresses, and turn off redirect following so an allowed URL cannot bounce to a blocked one. The happy path for real users is unchanged.

    A real case

    Capital One lost 100 million records to an SSRF on a WAF

    In 2019 an attacker used SSRF against a misconfigured Capital One WAF to reach the AWS instance metadata endpoint, stole the IAM role credentials, and exfiltrated 100 million customer records from S3.

    Related reading

    Glossary

    Server-Side Request Forgery (SSRF)Unvalidated Redirect (Open Redirect)Directory Traversal (Path Traversal)

    What we find

    injection vulnerabilities

    References

    • A10: Server-Side Request Forgery (SSRF) — official OWASP entry
    • OWASP Top 10 for Web Applications (2021) — full list
    • CWE-918 on cwe.mitre.org