SSRF

SSRF in Node.js Express: Remediation [GHSA-gjxx-92w9-8v8f]

[Updated March 2026] Updated GHSA-gjxx-92w9-8v8f

Overview

SSRF (server-side request forgery) occurs when an app makes HTTP requests on behalf of a client using a URL supplied by that client. In Node.js with Express, this often appears in routes that proxy external resources or perform remote validation based on user input. If an attacker can control the target URL, the server may reach internal networks, cloud metadata endpoints (for example, 169.254.169.254), or other services not intended for public access, potentially exposing data or enabling further attacks. Impact can range from information disclosure and internal IP address enumeration to accessing protected services and exfiltration. In cloud or containerized deployments, SSRF can bypass perimeter controls by leveraging the application as a proxy, potentially compromising the host or other services within the same network. The vulnerability often arises because input-driven requests are executed without proper host or resource isolation. Remediation should focus on defensive patterns rather than CVE specifics. Implement strict input validation, enforce a allowlist of permitted destinations, restrict protocols (http/https only), and validate URLs using a URL parser. Add network controls for outbound traffic, such as egress proxies or firewall rules, and monitor for suspicious fetch patterns. Include tests that attempt SSRF attacks to verify protections.

Code Fix Example

Node.js (Express) API Security Remediation
// Vulnerable pattern (SSRF risk)
const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.get('/fetch', async (req, res) => {
  const target = req.query.url;
  // Vulnerable: directly fetch URL supplied by client
  const r = await fetch(target);
  const text = await r.text();
  res.send(text);
});

// Fixed version: allowlist/validation and restricted outbound
const allowedHosts = ['example.com', 'api.example.com'];

app.get('/fetch-secure', async (req, res) => {
  const target = req.query.url;
  let urlObj;
  try {
    urlObj = new URL(target);
  } catch (e) {
    return res.status(400).send('Invalid URL');
  }
  if (!['http:', 'https:'].includes(urlObj.protocol)) {
    return res.status(400).send('Unsupported protocol');
  }
  if (!allowedHosts.includes(urlObj.hostname) && !urlObj.hostname.endsWith('.example.com')) {
    return res.status(403).send('Host not allowed');
  }
  const r = await fetch(urlObj.toString());
  const text = await r.text();
  res.send(text);
});

app.listen(3000, () => console.log('Server running on port 3000'));

CVE References

Choose which optional cookies to allow. You can change this any time.