Ssrf in Feathersjs with Jwt Tokens
Ssrf in Feathersjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Server-Side Request Forgery (SSRF) in a FeathersJS service that validates JWT tokens can occur when an attacker is able to influence an HTTP request made by the server while the request carries or depends on trusted identity information encoded in a JWT. Even though JWTs are typically used for authentication and authorization, they do not prevent the server from making arbitrary outbound HTTP calls. If your FeathersJS service accepts user-supplied URLs or host/port inputs and uses an HTTP client (for example axios or Node’s http/http-proxy) to make requests, an attacker can supply a malicious target that causes the server to interact with internal services.
Consider a FeathersJS hook that enforces JWT-based authentication and then forwards a request to a user-provided endpoint. The JWT may contain roles or scopes, but if the server uses the JWT only to extract identity and does not validate or restrict the destination, the server will make the request regardless of the token’s contents. Common patterns that create risk include dynamic base URLs, webhook endpoints, or service-discovery integrations where the token is trusted but the target is not. Because SSRF abuses the server’s network reachability, it can allow an attacker to probe internal metadata services (e.g., 169.254.169.254 on cloud environments), internal databases, or other APIs that the FeathersJS server can reach.
In a real FeathersJS setup, SSRF often maps to OWASP API Top 10 #1 (Broken Object Level Authorization) when combined with IDOR-like paths, and can intersect with BOLA if internal resources are accessed using user-controlled identifiers. Input validation and network-level restrictions are essential: JWT tokens confirm who is making the request, but they do not define where the server is allowed to connect. For example, an unauthenticated SSRF probe may not require a token at all if the endpoint is public, but a privileged internal probe may occur when an authenticated request with a valid JWT triggers server-side HTTP calls to internal addresses.
To illustrate, a vulnerable FeathersJS service might accept a url field from the request body and use it with an HTTP client while authenticated via JWT. An attacker could supply http://169.254.169.254/latest/meta-data/ as the URL. The server, executing the request with its own network permissions, may leak sensitive metadata even though the caller presented a valid token. The presence of JWT validation gives a false sense of boundary control, because the server-side request is not constrained by the token’s scopes unless explicitly enforced in application logic.
middleBrick detects SSRF by testing unauthenticated attack surfaces and, when spec-driven, cross-referencing OpenAPI definitions with runtime behavior. If your API specification describes dynamic target URLs or webhook configurations, ensure that destinations are limited to a strict allowlist and that JWT claims are used to authorize both the caller and the intended target. This prevents an authenticated request from becoming a pivot point for internal network exploration.
Jwt Tokens-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on two layers: (1) controlling and validating outbound destinations and (2) using JWT claims to authorize the request destination, not only the caller. Do not rely on authentication alone to prevent SSRF; apply explicit allowlisting and reject ambiguous or private targets.
First, avoid using raw user input as the request URL. Instead, use a fixed set of known endpoints or a validated configuration. When dynamic URLs are necessary, parse and validate the hostname against an allowlist of permitted domains or IP ranges, and reject private IP addresses (e.g., 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8).
Second, incorporate JWT claims to decide whether the request may proceed to a given target. For example, if a token contains a scope or permissions claim, ensure the requested action is explicitly permitted. Below are two concrete FeathersJS examples: one showing a vulnerable approach and one showing a hardened approach.
Vulnerable example (do not use):
const axios = require('axios');
app.use('/proxy', {
async create(data, params) {
const { url } = data; // user-controlled
const token = params.headers.authorization?.replace('Bearer ', '');
// JWT is verified by an authentication hook, but destination is not validated
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${token}` }
});
return response.data;
}
});
This pattern trusts the caller-supplied url and forwards the JWT unchanged, allowing SSRF against internal endpoints.
Hardened example:
const axios = require('axios');
const publicHosts = new Set(['api.example.com', 'cdn.example.com']);
function isValidHost(url) {
try {
const u = new URL(url);
if (!publicHosts.has(u.hostname)) return false;
// Reject private IPs
const ip = u.hostname; // in real use, resolve DNS if needed or use a denylist
// simplistic check for demonstration; prefer a library for IP validation
if (u.hostname.startsWith('10.') || u.hostname.startsWith('192.168.')) return false;
return true;
} catch {
return false;
}
}
app.use('/proxy', {
async create(data, params) {
const { url } = data;
const token = params.headers.authorization?.replace('Bearer ', '');
if (!isValidHost(url)) {
throw new Error('Request target not allowed');
}
// Optionally inspect JWT claims to further restrict targets
// e.g., if (tokenScopes.includes('proxy:external')) { ... }
const response = await axios.get(url, {
headers: { Authorization: `Bearer ${token}` },
timeout: 5000
});
return response.data;
}
});
In the hardened example, isValidHost ensures only approved domains are used, and private IPs are explicitly rejected. You can further refine authorization by inspecting JWT claims (e.g., a scope or roles claim) to decide whether the request may target a given category of endpoints. For higher assurance, resolve hostnames and compare against a strict inventory of allowed IPs or service names, and enforce timeouts to limit resource consumption.
middleBrick’s LLM/AI Security checks and OpenAPI/Swagger analysis can help identify dynamic URL parameters and webhook configurations that may introduce SSRF. Use the scanner to surface risky endpoints and then apply the above input validation and JWT-aware routing rules to mitigate the risk.
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |