SSRF Attacks Explained: How Attackers Reach Your Internal Network via Your App

SecureCodeReviews Team
March 1, 2026
13 min read
388 words
Share

What is SSRF?

Server-Side Request Forgery (SSRF) is a vulnerability where an attacker can make a server-side application send HTTP requests to an arbitrary destination — including internal services, cloud metadata endpoints, and other systems that should never be accessible from the internet.

SSRF was added to the OWASP Top 10 in 2021 as A10: Server-Side Request Forgery. It's one of the fastest-growing vulnerability categories, driven by cloud adoption and microservice architectures.


How SSRF Works

1. Attacker → Your App: "Fetch this URL: http://169.254.169.254/latest/meta-data/iam/security-credentials/"
2. Your App → Cloud Metadata API: Fetches the URL (server-side)
3. Cloud Metadata → Your App: Returns IAM credentials
4. Your App → Attacker: Forwards the response containing AWS keys

The key insight: your server trusts itself. Internal services, cloud metadata APIs, and admin panels that block external requests happily respond to requests from your own server.


Real-World SSRF Breaches

Capital One (2019) — $80M Fine, 100M Records

The attacker exploited an SSRF vulnerability in a WAF (Web Application Firewall) to access AWS metadata at 169.254.169.254, obtained IAM role credentials, and used them to exfiltrate 100+ million customer records from S3.

Attack chain:

  1. SSRF to http://169.254.169.254/latest/meta-data/iam/security-credentials/
  2. Retrieved temporary AWS credentials
  3. Used aws s3 sync to download all customer data
  4. 100M credit card applications, 140,000 SSNs, 80,000 bank account numbers

GitLab (2021) — Critical SSRF

GitLab's import feature allowed users to specify a URL to import a project from. The URL wasn't properly validated, allowing attackers to:

  • Access internal GitLab services
  • Read AWS/GCP metadata
  • Scan internal network ports

Common SSRF Patterns

// Slack-style link preview
app.post('/api/preview', async (req, res) => {
  const { url } = req.body;
  const response = await fetch(url); // SSRF!
  const html = await response.text();
  const title = html.match(/<title>(.*?)<\/title>/)?.[1];
  res.json({ title, url });
});

Pattern 2: Webhook / Callback URLs

// Payment webhook
app.post('/api/webhooks/configure', async (req, res) => {
  const { callbackUrl } = req.body;
  // Attacker sets callbackUrl to http://169.254.169.254/...
  await fetch(callbackUrl, { method: 'POST', body: testPayload }); // SSRF!
});

Pattern 3: Image / File Processing

// Image resizer that fetches from URL
app.post('/api/resize', async (req, res) => {
  const { imageUrl, width, height } = req.body;
  const image = await fetch(imageUrl); // SSRF!
  const buffer = await image.buffer();
  const resized = await sharp(buffer).resize(width, height).toBuffer();
  res.send(resized);
});

Pattern 4: PDF Generation

// HTML-to-PDF with server-side rendering
app.post('/api/generate-pdf', async (req, res) => {
  const { htmlUrl } = req.body;
  // Puppeteer navigates to the URL server-side
  await page.goto(htmlUrl); // SSRF!
  const pdf = await page.pdf();
  res.send(pdf);
});

SSRF Defense in Depth

Layer 1: URL Validation

const { URL } = require('url');
const dns = require('dns').promises;
const net = require('net');

async function isSafeUrl(urlString) {
  let parsed;
  try {
    parsed = new URL(urlString);
  } catch {
    return false;
  }

  // Block non-HTTP protocols
  if (!['http:', 'https:'].includes(parsed.protocol)) return false;

  // Block known dangerous hostnames
  const blocked = ['localhost', '127.0.0.1', '0.0.0.0', '[::1]', 'metadata.google.internal'];
  if (blocked.includes(parsed.hostname)) return false;

  // Resolve DNS and check actual IP
  const addresses = await dns.resolve4(parsed.hostname).catch(() => []);
  for (const addr of addresses) {
    if (net.isIP(addr) && isPrivateIP(addr)) return false;
  }

  return true;
}

function isPrivateIP(ip) {
  const parts = ip.split('.').map(Number);
  return (
    parts[0] === 10 ||
    parts[0] === 127 ||
    (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) ||
    (parts[0] === 192 && parts[1] === 168) ||
    (parts[0] === 169 && parts[1] === 254)
  );
}

Layer 2: AWS IMDSv2 (Block Metadata SSRF)

# Require IMDSv2 (token-based) on all EC2 instances
aws ec2 modify-instance-metadata-options \
  --instance-id i-1234567890 \
  --http-tokens required \
  --http-put-response-hop-limit 1

Layer 3: Network Segmentation

  • Run user-facing applications in a VPC with no direct access to internal services
  • Use security groups to block outbound connections to metadata endpoints
  • Use a proxy/gateway for all outbound requests with URL allowlisting

Layer 4: Response Validation

Even if an SSRF request succeeds, don't return the raw response:

// Only return specific, expected fields
const response = await safeFetch(url);
const data = await response.json();
res.json({
  title: typeof data.title === 'string' ? data.title.slice(0, 200) : '',
  description: typeof data.description === 'string' ? data.description.slice(0, 500) : '',
});

SSRF Testing Checklist

TestMethod
Cloud metadataTry http://169.254.169.254/latest/meta-data/ (AWS), http://metadata.google.internal/ (GCP)
Internal servicesTry http://localhost:PORT for common ports (3000, 5432, 6379, 8080)
DNS rebindingRegister a domain that alternates between public and private IPs
Protocol smugglingTry file:///etc/passwd, gopher://, dict://
Redirect bypassHost a URL that 302-redirects to http://169.254.169.254/

How We Help

Our penetration testing and code review services include comprehensive SSRF testing. We check every server-side URL fetch, webhook handler, and external integration for SSRF vulnerabilities.

Request a Free Sample Code Review → | Schedule a Pentest →

Editorial standards

Published by SecureCodeReviews

This article is part of our original AI security and cybersecurity content library. We show publish and update dates, keep company and policy pages public, and update important guidance when material changes affect readers.

Named author: SecureCodeReviews Team
Published: Mar 1, 2026
Update status: current publication version

Questions or corrections?

Review our editorial standards, learn more about the company, or contact us if a page needs clarification.

AI Security Audit

Planning an AI feature launch or security review?

We assess prompt injection paths, data leakage, tool use, access control, and unsafe AI workflows before they become production problems.

Manual review for agent, prompt, and retrieval attack paths
Actionable remediation guidance for your AI stack
Coverage for LLM apps, MCP integrations, and internal AI tools

Talk to SecureCodeReviews

Get a scoped review path fast

Manual review
Actionable fixes
Fast turnaround
Security-focused

Advertisement