SSRF

SSRF in NestJS: Secure External Fetches [March 2026] [GHSA-89v5-38xr-9m4j]

[Updated March 2026] Updated GHSA-89v5-38xr-9m4j

Overview

SSRF vulnerabilities in NestJS apps arise when an endpoint accepts a URL from a client and fetches that resource server-side. An attacker can cause the application to reach internal resources, cloud metadata endpoints, or services behind a firewall, potentially exfiltrating data or mapping the network. In cloud deployments, this can enable access to the instance metadata service (IMDS), internal dashboards, or private microservices, leading to data leakage, lateral movement, or broader compromise. In NestJS, SSRF commonly manifests through the HttpService (Axios) usage where user-controlled URLs are fetched directly, or through downstream libraries that resolve user-supplied endpoints. If requests target internal addresses (such as 10.x.x.x, 192.168.x.x, or 169.254.169.254) or rely on redirects, an attacker can induce the server to reach sensitive resources or perform unauthorized scanning. Best practices to remediate include validating and whitelisting URLs, restricting protocols to http/https, enforcing timeouts and disabling redirects, and centralizing outbound HTTP logic behind a constrained service. Also avoid using client-supplied URLs to reach internal services; prefer a proxy or gateway with an allowlist of allowed endpoints, and implement robust input validation. After applying fixes, add automated tests simulating malicious URLs, enable monitoring for outbound requests to blocked hosts, and review logs to detect SSRF attempts. Combine these with code review and secure development practices to reduce recurrence.

Code Fix Example

NestJS API Security Remediation
import { Injectable } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import axios from 'axios';

// Vulnerable pattern: directly using user-provided URL with HttpService
@Injectable()
export class VulnerableFetcher {
  constructor(private readonly http: HttpService) {}

  async fetch(url: string): Promise<any> {
    const resp = await firstValueFrom(this.http.get(url));
    return resp.data;
  }
}

// Fixed pattern: validate URL against an allowlist and enforce safe HttpService usage
@Injectable()
export class SafeFetcher {
  private readonly allowList = new Set<string>([
    'https://api.example.com',
    'https://external-service.example.org',
  ]);
  private http: HttpService;

  constructor() {
    const axiosInstance = axios.create({ timeout: 5000, maxRedirects: 0 });
    this.http = new HttpService(axiosInstance);
  }

  private isAllowed(urlStr: string): boolean {
    try {
      const u = new URL(urlStr);
      if (!['http:', 'https:'].includes(u.protocol)) return false;
      return this.allowList.has(u.origin);
    } catch {
      return false;
    }
  }

  async fetch(url: string): Promise<any> {
    if (!this.isAllowed(url)) {
      throw new Error('URL not allowed');
    }
    const resp = await firstValueFrom(this.http.get(url));
    return resp.data;
  }
}

CVE References

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