Ssrf in Express with Api Keys
Ssrf in Express with Api Keys — how this specific combination creates or exposes the vulnerability
Server-Side Request Forgery (SSRF) in Express applications that rely on API keys creates a compound risk: the server-side code makes outbound HTTP calls on behalf of clients, and those calls often include static or semi-static API keys. When an attacker can influence the target URL of those outbound requests, they can force the server to make requests to internal services or metadata endpoints, using the server’s identity and included credentials. In Express, this commonly arises when a route accepts a user-supplied URL or host and passes it to an HTTP client (for example, to call a third-party API), while also attaching an API key in headers or as a query parameter.
Consider an Express route that fetches external data and forwards an API key in the Authorization header:
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
app.get('/proxy', async (req, res) => {
const { url } = req.query;
const apiKey = process.env.EXTERNAL_API_KEY;
try {
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${apiKey}` }
});
res.json(response.data);
} catch (error) {
res.status(502).json({ error: 'Bad gateway' });
}
});
app.listen(3000);
In this pattern, the user-controlled url query parameter is passed directly to axios.get. An attacker can supply an internal address such as http://169.254.169.254/latest/meta-data/iam/security-credentials/ (AWS instance metadata). Because the request originates from the server and includes EXTERNAL_API_KEY in headers, the SSRF can be used to probe internal networks or cloud metadata services, effectively chaining the server’s trust in API keys with a lack of destination validation.
SSRF in this context is not only about reaching internal endpoints; it also involves abusing the API key’s scope. If the key is over-privileged (e.g., has write permissions), the forced request may perform unintended actions on behalf of the server. Moreover, some APIs rate-limit or throttle by key, so an attacker can cause denial-of-service via the server’s key. The vulnerability arises from accepting a raw URL and from not validating or restricting destinations, while the presence of API keys makes the server a more attractive proxy for lateral movement or data exfiltration.
MiddlewareBrick scans identify SSRF in Express endpoints that incorporate outbound HTTP calls and API keys by correlating unvalidated URL inputs with outbound requests and detecting patterns such as unsanitized query parameters and key-bearing headers. Findings include evidence of how an attacker could leverage the server-side key to reach sensitive internal services, alongside mapping to relevant OWASP API Top 10 items and compliance references.
Api Keys-Specific Remediation in Express — concrete code fixes
To mitigate SSRR in Express when using API keys, remove user control over the request target, enforce allowlists, and avoid forwarding raw URLs. Below are concrete, safe patterns that keep API key usage while reducing risk.
1. Use a fixed target with path-based routing
If you only need to call a known third-party API, encode the base URL on the server and let users supply only safe path segments or parameters:
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
const BASE = 'https://api.example.com/v1';
app.get('/resource/:id', async (req, res) => {
const apiKey = process.env.EXTERNAL_API_KEY;
const id = req.params.id;
try {
const response = await axios.get(`${BASE}/resource/${encodeURIComponent(id)}`, {
headers: { Authorization: `Bearer ${apiKey}` }
});
res.json(response.data);
} catch (error) {
res.status(502).json({ error: 'Bad gateway' });
}
});
app.listen(3000);
2. Validate and restrict destinations (explicit allowlist)
If you must accept a target, validate it against an allowlist of permitted hosts and reject anything that does not match:
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
const ALLOWED_HOSTS = new Set(['api.example.com', 'data.example.com']);
app.get('/proxy', async (req, res) => {
const apiKey = process.env.EXTERNAL_API_KEY;
const targetUrl = new URL(req.query.url, 'https://example.com');
if (!ALLOWED_HOSTS.has(targetUrl.hostname)) {
return res.status(400).json({ error: 'Host not allowed' });
}
try {
const response = await axios.get(targetUrl.toString(), {
headers: { Authorization: `Bearer ${apiKey}` }
});
res.json(response.data);
} catch (error) {
res.status(502).json({ error: 'Bad gateway' });
}
});
app.listen(3000);
3. Avoid exposing API keys in browser-facing code
Ensure API keys are stored in environment variables and never sent to the client. Do not concatenate keys into query strings that may leak in logs or browser history. When calling third-party services server-side, keep key scope minimal (read-only when possible) and rotate regularly.
4. Use proxy configuration instead of forwarding user URLs
For scenarios where you need to route to multiple backends, configure routing on the server without user-supplied URLs. Use path-based routing rules to determine the backend, and keep the destination fixed:
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
const services = {
serviceA: 'https://sa.example.com',
serviceB: 'https://sb.example.com'
};
app.post('/call/:service/action', async (req, res) => {
const apiKey = process.env.EXTERNAL_API_KEY;
const backend = services[req.params.service];
if (!backend) {
return res.status(400).json({ error: 'Unknown service' });
}
try {
const response = await axios.post(`${backend}/action`, req.body, {
headers: { Authorization: `Bearer ${apiKey}` }
});
res.json(response.data);
} catch (error) {
res.status(502).json({ error: 'Bad gateway' });
}
});
app.listen(3000);
These patterns ensure API keys remain on the server while preventing SSRF by removing attacker-controlled destination URLs or strictly constraining them.
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |