Overview
SSRF vulnerabilities occur when a server-side application fetches resources based on user input. In Node.js (Express) this commonly happens when an endpoint accepts a URL and proxies or fetches it without validation. An attacker could use such a path to reach internal services, cloud metadata endpoints, or other protected resources that are not publicly reachable. Without proper controls, this can lead to exposure of sensitive data, internal network access, or user isolation violations.
Code Fix Example
Node.js (Express) API Security Remediation
// Vulnerable pattern
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/proxy', async (req, res) => {
const url = req.query.url;
if (!url) return res.status(400).send('url is required');
// Vulnerable: directly fetch user-supplied URL
try {
const r = await axios.get(url, { timeout: 5000 });
res.set('Content-Type', r.headers['content-type'] || 'text/plain');
res.send(r.data);
} catch (e) {
res.status(502).send('Error fetching URL');
}
});
app.listen(3000, () => console.log('Listening on 3000'));
// Fixed pattern (brief separation for clarity)
const ALLOWED_ORIGINS = [
'https://example.com',
'https://api.example.org',
'https://mycdn.example.net'
];
app.get('/proxy', async (req, res) => {
const urlStr = req.query.url;
if (!urlStr) return res.status(400).send('url is required');
let url;
try {
url = new URL(urlStr);
} catch {
return res.status(400).send('Invalid URL');
}
// Apply allowlist based on origin
if (!ALLOWED_ORIGINS.includes(url.origin)) {
return res.status(403).send('Forbidden');
}
try {
const r = await axios.get(url.toString(), { timeout: 5000 });
res.set('Content-Type', r.headers['content-type'] || 'text/plain');
res.send(r.data);
} catch (e) {
res.status(502).send('Error fetching URL');
}
});