JAVASCRIPT

Prevent Server-Side Request Forgery (SSRF) Attacks

Protect your server from SSRF vulnerabilities by validating URLs and restricting outbound requests to untrusted or internal networks in Node.js applications.

// Install: npm install url is-localhost-ip axios
const { URL } = require('url');
const isLocalhostIp = require('is-localhost-ip'); // To check loopback and private IPs
const axios = require('axios');

function isValidOutgoingUrl(inputUrl) {
  try {
    const url = new URL(inputUrl);
    
    // Block non-HTTP/HTTPS schemes
    if (!['http:', 'https:'].includes(url.protocol)) {
      return false;
    }
    
    // Block requests to localhost or private IPs (SSRF prevention)
    // This is a crucial step to prevent access to internal services.
    if (isLocalhostIp(url.hostname)) {
      console.warn(`SSRF attempt blocked: Localhost IP detected for ${url.hostname}`);
      return false;
    }
    
    // Optional: Block specific internal IP ranges if `is-localhost-ip` isn't enough for your network
    // Example: Block 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
    // You'd need a more robust IP range checker for this.
    
    // Further validation: Ensure the hostname is not a literal IP if not intended
    // or check against a whitelist of allowed domains.
    // For simplicity, we assume any public domain is acceptable after prior checks.
    
    return true;
  } catch (e) {
    console.error("URL parsing error:", e.message);
    return false;
  }
}

async function makeSafeHttpRequest(targetUrl) {
  if (!isValidOutgoingUrl(targetUrl)) {
    console.error(`Blocked unsafe URL: ${targetUrl}`);
    throw new Error('Unsafe URL detected. Request blocked.');
  }

  try {
    const response = await axios.get(targetUrl, { timeout: 5000 }); // Add timeout for safety
    return response.data;
  } catch (error) {
    console.error(`Error making HTTP request to ${targetUrl}:`, error.message);
    throw new Error('Failed to fetch data from external URL.');
  }
}

// Example usage:
/*
async function runSSRFTest() {
  // Safe URL
  try {
    const publicData = await makeSafeHttpRequest('https://jsonplaceholder.typicode.com/todos/1');
    console.log('Public data fetched:', publicData);
  } catch (e) {
    console.error(e.message);
  }

  // Unsafe URL (example of a blocked internal IP)
  try {
    const internalData = await makeSafeHttpRequest('http://127.0.0.1:8000/internal-api'); // Loopback IP
    console.log('Internal data fetched:', internalData);
  } catch (e) {
    console.error(e.message);
  }

  try {
    const privateIpData = await makeSafeHttpRequest('http://192.168.1.1/admin'); // Private IP
    console.log('Private IP data fetched:', privateIpData);
  } catch (e) {
    console.error(e.message);
  }
}

runSSRFTest();
*/
How it works: Server-Side Request Forgery (SSRF) allows attackers to trick a server into making requests to internal or other sensitive systems. This snippet provides a robust `isValidOutgoingUrl` function that validates a URL before the server initiates an outgoing request. It blocks non-HTTP/HTTPS schemes and, critically, uses `is-localhost-ip` to prevent requests to loopback addresses (`127.0.0.1`, `::1`) and known private IP ranges (e.g., `10.x.x.x`, `192.168.x.x`). This prevents an attacker from using your server as a proxy to scan internal networks or access sensitive resources. A timeout is also added to the request to prevent long-running or hanging connections.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs