High Severity CWE-918 OWASP A10:2021

SSRF: When Your Server Makes Requests for Attackers

Attackers use your server to access internal systems they can't reach directly

Quick Answer

SSRF (Server-Side Request Forgery) lets attackers make your server send requests to internal systems they can't reach directly. Next.js had critical SSRF vulnerabilities in 2024-2025. Always validate and allowlist URLs before your server fetches them.

What is SSRF?

SSRF (Server-Side Request Forgery) happens when attackers trick your server into making HTTP requests on their behalf. Your server has access to internal systems - localhost services, cloud metadata endpoints, private APIs - that attackers can't reach directly from the internet. By controlling a URL your server fetches, they gain access to these protected resources.

Think of it like convincing a delivery driver who has access to a secure building to pick up a package for you from inside. The driver (your server) has legitimate access; the attacker just manipulates what they retrieve.

SSRF is serious enough to have its own dedicated category in the OWASP Top 10 (A10:2021). The consequences include accessing internal APIs, stealing cloud credentials, and scanning internal networks - all from a simple URL parameter.

Real Next.js SSRF Vulnerabilities

This isn't theoretical - Next.js had critical SSRF vulnerabilities that affected real applications:

CVE-2024-34351 Critical

Affected: Next.js 13.4.0 - 14.1.0 (Server Actions)

Condition: Self-hosted deployments using Server Actions with redirect()

Attack: Manipulating the Host header to trigger SSRF via the redirect function

Fixed: Next.js 14.1.1+

View Advisory
CVE-2025-57822 High

Affected: Next.js < 14.2.32 and < 15.4.7

Condition: Middleware using next() function

Fixed: Next.js 14.2.32+ and 15.4.7+

View Advisory

If you're a vibe coder using Next.js, check your package.json version immediately. Run npm show next version to see available updates.

Common SSRF Attack Patterns

Attackers have many targets once they can control server-side requests:

Internal Service Access

http://localhost:3000/admin

Access admin panels and internal APIs running on the same server.

Cloud Metadata Theft

http://169.254.169.254/latest/meta-data/

AWS, GCP, and Azure expose IAM credentials at this endpoint. SSRF can steal them.

File Protocol

file:///etc/passwd

Read local files if the HTTP library supports file:// protocol.

Internal Network Scanning

http://192.168.1.1:22/

Port scan internal hosts to discover services.

DNS Rebinding

http://attacker-domain.com/

External domain that resolves to internal IP after initial check.

For detailed exploitation techniques, see the PortSwigger SSRF Guide.

Why AI Tools Generate SSRF-Vulnerable Code

When you ask AI tools to add features like "preview a URL" or "fetch data from a webhook," they generate the simplest working code without URL validation. This is classic vibe coding risk - the AI gives you functional code that's insecure by default.

Features commonly vibe coded with SSRF vulnerabilities:

  • URL preview/unfurling - "Add link previews to my chat app"
  • Image proxy - "Resize images from any URL"
  • Webhook handlers - "Add webhook support"
  • Import from URL - "Let users import data from a URL"
  • PDF generation - "Generate PDFs from URLs"

AI-generated code for these features almost never includes URL validation because you didn't explicitly ask for it.

Vulnerable Code Examples

Pattern 1: Vulnerable URL Fetch (AI Default)

Vulnerable
// AI generates this for "fetch URL" features
app.get('/preview', async (req, res) => {
  const url = req.query.url
  const response = await fetch(url) // DANGEROUS!
  const data = await response.text()
  res.send(data)
})

// Attacker uses: /preview?url=http://localhost:3000/admin
// Or: /preview?url=http://169.254.169.254/latest/meta-data/

Any URL accepted, including internal services and cloud metadata endpoints. The attacker steals IAM credentials from AWS.

Pattern 2: Vulnerable Image Proxy

Vulnerable
// Image proxy without validation
app.get('/image-proxy', async (req, res) => {
  const imageUrl = req.query.src
  const response = await fetch(imageUrl)
  const buffer = await response.arrayBuffer()
  res.set('Content-Type', response.headers.get('content-type'))
  res.send(Buffer.from(buffer))
})

// Attacker: /image-proxy?src=file:///etc/passwd

Image proxies are common SSRF vectors. No protocol or domain validation allows file:// and internal URL access.

How to Fix SSRF

Secure Pattern: Complete URL Validation

Secure
const dns = require('dns').promises

const ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com']
const ALLOWED_PROTOCOLS = ['https:']

// Block internal IP ranges
const BLOCKED_IP_PATTERNS = [
  /^localhost$/i,
  /^127\./,
  /^10\./,
  /^172\.(1[6-9]|2[0-9]|3[0-1])\./,
  /^192\.168\./,
  /^169\.254\./, // AWS/Azure metadata
  /^0\.0\.0\.0$/,
]

async function validateUrl(urlString) {
  // Parse URL safely
  let parsed
  try {
    parsed = new URL(urlString)
  } catch {
    throw new Error('Invalid URL')
  }

  // Check protocol
  if (!ALLOWED_PROTOCOLS.includes(parsed.protocol)) {
    throw new Error('Only HTTPS allowed')
  }

  // Check domain allowlist
  if (!ALLOWED_DOMAINS.includes(parsed.hostname)) {
    throw new Error('Domain not allowed')
  }

  // Check hostname isn't internal
  if (BLOCKED_IP_PATTERNS.some(p => p.test(parsed.hostname))) {
    throw new Error('Internal address blocked')
  }

  // DNS resolution check - prevents rebinding
  const ips = await dns.resolve4(parsed.hostname)
  for (const ip of ips) {
    if (BLOCKED_IP_PATTERNS.some(p => p.test(ip))) {
      throw new Error('Internal address blocked')
    }
  }

  return parsed.href
}

app.get('/preview', async (req, res) => {
  try {
    const safeUrl = await validateUrl(req.query.url)
    const response = await fetch(safeUrl)
    const data = await response.text()
    res.send(data)
  } catch (err) {
    res.status(403).json({ error: err.message })
  }
})

Complete validation: protocol allowlist, domain allowlist, IP blocking, and DNS resolution check to catch rebinding attacks.

Next.js Server Action Protection

Secure
// Secure Next.js Server Action
'use server'

import { redirect } from 'next/navigation'

const ALLOWED_PATHS = ['/dashboard', '/profile', '/settings']

export async function safeRedirect(path: string) {
  // Block external redirects
  if (path.startsWith('http://') || path.startsWith('https://')) {
    throw new Error('External redirects not allowed')
  }

  // Block protocol-relative URLs
  if (path.startsWith('//')) {
    throw new Error('Protocol-relative URLs not allowed')
  }

  // Allowlist internal paths
  if (!ALLOWED_PATHS.some(allowed => path.startsWith(allowed))) {
    throw new Error('Invalid redirect path')
  }

  redirect(path)
}

Server Actions should validate redirect paths strictly. This prevents CVE-2024-34351-style attacks.

Key Security Points

  • Always use an allowlist - specify exactly which domains are allowed
  • Block all internal IPs - 127.x, 10.x, 172.16-31.x, 192.168.x, 169.254.x
  • Only allow HTTPS - block file://, gopher://, and other protocols
  • Check DNS resolution - catch rebinding attacks where domain resolves to internal IP
  • Don't trust redirects - an allowed domain could redirect to internal URL

AI Fix Prompt

Copy this prompt to your AI tool to scan your codebase for SSRF vulnerabilities:

You are a security auditor. Scan this codebase for SSRF (Server-Side Request Forgery) vulnerabilities (CWE-918).

## What to Search For

### 1. HTTP Requests with User-Controlled URLs
Search for fetch(), axios, got, node-fetch, or http/https modules with dynamic URLs:
- fetch(req.query.url)
- axios.get(userProvidedUrl)
- got(urlFromRequest)
- http.request(userInput)

### 2. Common SSRF-Vulnerable Features
Look for these features that commonly have SSRF:
- URL preview / link unfurling
- Image proxy / image resizer
- Webhook receivers and senders
- PDF generators that fetch URLs
- Import from URL functionality
- RSS feed readers
- OAuth callbacks

### 3. Next.js Specific Patterns

**Server Actions with redirects:**
```typescript
// CHECK: Does this validate the redirect target?
'use server'
import { redirect } from 'next/navigation'
export async function action(url: string) {
  redirect(url) // SSRF if url not validated
}
```

**API routes fetching URLs:**
```typescript
// app/api/proxy/route.ts
export async function GET(request: NextRequest) {
  const url = request.nextUrl.searchParams.get('url')
  const response = await fetch(url) // VULNERABLE!
}
```

### 4. URL Validation to Verify

For any URL fetching, verify ALL of these exist:
```javascript
// 1. Parse URL safely
const parsed = new URL(userUrl)

// 2. Protocol allowlist
if (!['https:'].includes(parsed.protocol)) reject

// 3. Domain allowlist OR blocklist check
if (!ALLOWED_DOMAINS.includes(parsed.hostname)) reject

// 4. Block internal IPs (including cloud metadata)
const blocked = [
  /^localhost$/i,
  /^127\./,
  /^10\./,
  /^172\.(1[6-9]|2[0-9]|3[0-1])\./,
  /^192\.168\./,
  /^169\.254\./, // AWS/Azure metadata
  /^0\.0\.0\.0$/,
  /^\[::1\]$/, // IPv6 localhost
]
if (blocked.some(p => p.test(parsed.hostname))) reject

// 5. DNS resolution check (prevents rebinding)
const ips = await dns.resolve4(parsed.hostname)
for (const ip of ips) {
  if (blocked.some(p => p.test(ip))) reject
}
```

### 5. Bypass Attempts to Consider

Check if validation can be bypassed:
- IP address representations: 0177.0.0.1 (octal), 0x7f.0.0.1 (hex)
- URL encoding: %31%32%37.0.0.1
- DNS rebinding: external domain resolving to internal IP
- Redirects: allowed domain redirects to internal URL
- IPv6: [::1], [0:0:0:0:0:0:0:1]
- Decimal IP: 2130706433 (= 127.0.0.1)

## Framework Version Checks

For Next.js projects, verify version is patched:
- CVE-2024-34351: Fixed in 14.1.1+
- CVE-2025-57822: Fixed in 14.2.32+ and 15.4.7+

Check package.json for Next.js version.

## Report Format

For each finding, report:
1. File and line number
2. The vulnerable code pattern
3. What user input controls the URL
4. What validation exists (if any)
5. What's missing (protocol check, domain allowlist, IP block, DNS check)
6. Severity (Critical if no validation, High if partial)
7. Specific fix with code

Frequently Asked Questions

What is SSRF vulnerability?

SSRF (Server-Side Request Forgery) is a vulnerability where attackers trick your server into making HTTP requests to internal systems they cannot reach directly. By controlling a URL your server fetches, attackers can access internal APIs, cloud metadata endpoints, or scan internal networks. SSRF has its own category in OWASP Top 10 (A10:2021).

How do attackers exploit SSRF?

Attackers find features where your server fetches URLs - like image previews, URL metadata extraction, or webhooks. They submit malicious URLs pointing to internal services (http://localhost:3000/admin) or cloud metadata (http://169.254.169.254). Your server, which has network access to these resources, fetches the data and returns it to the attacker.

Is my Next.js app vulnerable to SSRF?

Possibly. Next.js had critical SSRF vulnerabilities: CVE-2024-34351 (versions 13.4.0-14.1.0) in Server Actions and CVE-2025-57822 in middleware. Update to Next.js 14.1.1+ or 15.4.7+ and audit any code that fetches user-supplied URLs. Self-hosted deployments were more affected than Vercel-hosted apps.

How do I prevent SSRF in Node.js?

Implement URL allowlisting: only allow requests to approved domains. Block internal IP ranges (127.x.x.x, 10.x.x.x, 172.16-31.x.x, 192.168.x.x, 169.254.x.x). Only allow https:// protocol. Resolve DNS and check the resolved IP is not internal. Never pass user input directly to fetch() or HTTP libraries.

What can attackers access with SSRF?

Attackers can access: internal APIs and admin panels (localhost services), cloud metadata endpoints to steal IAM credentials (AWS 169.254.169.254), internal databases, configuration services, and can port-scan your internal network. On cloud platforms, SSRF can lead to complete account takeover via stolen credentials.

Related Security Topics

External Resources

Find SSRF Vulnerabilities in Your Code

VibeShip Scanner automatically detects SSRF patterns in your codebase, including unvalidated URL fetching and missing internal IP blocks.

Scan Your Code Free