Ssrf in Express with Bearer Tokens
Ssrf in Express with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Server-Side Request Forgery (SSRF) in Express applications that rely on Bearer Tokens for authorization can arise when an endpoint accepts a user-supplied URL and makes an outbound HTTP request using a fixed or proxied Authorization header. For example, a service might accept a target URL and an optional bearer_token, then forward that token to the target. If the token is not strictly scoped and the endpoint does not validate or restrict the destination, an attacker can supply an internal address (such as http://169.254.169.254/latest/meta-data/iam/security-credentials/ on AWS) and cause the server to relay its own credentials or metadata using the supplied Bearer Token or the service’s own identity.
In Express, this often manifests in a route like the following vulnerable pattern:
app.get('/fetch', async (req, res) => {
const { url, bearer_token } = req.query;
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${bearer_token || process.env.SERVICE_TOKEN}` }
});
res.json(response.data);
});
If url is user-controlled and not validated, an attacker can direct the request to an internal metadata service. Even when a Bearer Token is provided explicitly, the token may be overly privileged (e.g., having read access to sensitive resources) and be reused across services. SSRF in this context therefore exposes not only internal endpoints but also the risk of credential exfiltration or privilege escalation via the token. In OpenAPI/Swagger-driven documentation, such an endpoint might describe a generic proxy operation without clarifying that the target must be restricted to public, non-sensitive domains, which increases the chance of accidental exposure during development or integration.
Because middleBrick scans unauthenticated attack surfaces, it can detect SSRF patterns in Express routes that accept URLs and authorization headers without proper allowlisting. Findings include missing input validation, overly broad host resolution, and the presence of Authorization headers in outbound calls that can be manipulated by an attacker. These observations align with the OWASP API Top 10 category 'Broken Object Level Authorization' and common SSRF vectors, emphasizing the need to treat user-supplied URLs and tokens with strict constraints rather than assuming Bearer Tokens alone provide isolation.
Bearer Tokens-Specific Remediation in Express — concrete code fixes
Remediation focuses on input validation, avoiding automatic propagation of Bearer Tokens, and enforcing strict network boundaries. Do not forward user-supplied URLs directly, and do not automatically inject a Bearer Token derived from user input. Instead, use allowlists, remove unnecessary credentials, and apply explicit host and path validation.
Secure Express route with allowlisted hosts and no automatic token forwarding
const axios = require('axios');
const ALLOWED_HOSTS = new Set([
'api.example.com',
'cdn.example.com'
]);
function isAllowedHost(urlString) {
try {
const u = new URL(urlString);
return ALLOWED_HOSTS.has(u.hostname);
} catch {
return false;
}
}
app.get('/fetch', async (req, res) => {
const { url } = req.query;
if (!url || !isAllowedHost(url)) {
return res.status(400).json({ error: 'Invalid or disallowed URL' });
}
// Use a service-specific token scoped for this operation, not user-supplied
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${process.env.SERVICE_TOKEN}` }
});
res.json(response.data);
});
If your integration must accept a Bearer Token from a trusted admin (not from the request), ensure it is passed explicitly via a secure channel, never derived from user-controlled parameters, and used only with endpoints that enforce strict path and scope checks:
app.post('/admin/proxy', async (req, res) => {
// Admin-supplied token via authenticated management interface
const { admin_token } = req.body;
const { url } = req.query;
if (!admin_token || !isValidAdminToken(admin_token)) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!url || !isAllowedHost(url)) {
return res.status(400).json({ error: 'Invalid target' });
}
// Use the admin-provided token only for this controlled flow
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${admin_token}` }
});
res.json(response.data);
});
Additional hardening includes disabling redirect following for outbound requests (to prevent open redirects and SSRF chains), setting timeouts, and avoiding the use of generic proxy patterns in APIs that do not strictly require them. middleBrick’s scans can surface routes that accept URLs and include Authorization headers, helping you identify endpoints that require these remediation steps and map findings to frameworks such as OWASP API Top 10 and PCI-DSS.
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |