Ssrf in Feathersjs with Hmac Signatures
Ssrf in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
Server-Side Request Forgery (SSRF) in a FeathersJS service that uses Hmac Signatures arises when an attacker can influence an outgoing request while the service signs that request with a shared secret. FeathersJS does not provide built-in HTTP client logic; the vulnerability typically occurs in a custom service hook, event handler, or a wrapped external HTTP library where you build an outbound request and then apply an Hmac signature. If user-controlled data (e.g., a URL parameter, header, or body field) contributes to the target endpoint, the request path, or the signature components, an attacker may coerce the server to sign and forward requests to internal or unexpected destinations.
Consider a FeathersJS service that proxies signed requests to a backend API. The service builds a canonical string from selected fields, signs it with an Hmac using a secret, and then sends the request. If the destination URL or a critical header (such as X-Resource-ID) is derived from user input and included in the canonical string, an attacker can supply a value like http://169.169.169.200/latest/meta-data/iam/security-credentials/ (AWS instance metadata) or an internal service URL. The server will still compute a valid Hmac because the attacker-controlled portion is part of the signed material, and the outbound request will be dispatched to the malicious or internal endpoint. This is SSRF via signed requests: the signature provides authenticity, but the logic does not validate or restrict where the signed request may go.
In FeathersJS, this often manifests in hooks where you augment the data object before calling an external client. For example, if you dynamically set the URL, method, or headers from params and then compute an Hmac over selected fields (including potentially the URL), the server trusts the supplied values because they participate in the signature. An adversary who can influence any signed input can force the server to relay requests to internal endpoints, perform SSRF-based port scanning, or interact with metadata services. Because the signature validates the content rather than the destination, the server may assume the request is safe and proceed, effectively turning the SSRF into a signed, authenticated outbound call.
Real-world attack patterns include probing internal networks, reaching cloud metadata endpoints, or abusing internal APIs that assume mutual TLS or network-level isolation. Even if the Hmac protects data integrity, it does not replace network-level validation of the target. Without explicit allowlists for hostnames and ports, and without removing sensitive inputs from the signed canonical string, FeathersJS services can unintentionally become SSRF proxies that carry signed requests to undesirable locations.
To detect this class of issue in a black-box scan, middleBrick tests whether user-influenced data can affect the request target while still producing a valid Hmac. It checks whether the canonicalization logic includes attacker-controlled fields that should be excluded from signing, and whether outbound destinations are constrained by a strict allowlist. Remediation focuses on separating the data that determines the destination from the data that is signed, validating and restricting destinations, and ensuring that signatures cover only integrity-sensitive fields, not the target endpoint.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation centers on ensuring that the destination and routing information are never derived from untrusted input and are excluded from the Hmac canonical string. You should validate and restrict outbound targets using an allowlist, and only sign fields that must be integrity-protected. Below are concrete, syntactically correct FeathersJS hook examples that demonstrate a vulnerable pattern and a remediated pattern.
Vulnerable pattern: user-influenced URL and signed canonical
const crypto = require('crypto');
const axios = require('axios');
// WARNING: This is vulnerable to SSRF via Hmac-signed requests
app.service('proxy').hooks({
before: {
async create(data, context) {
const { url, method = 'GET', body, targetResourceId } = data;
// targetResourceId comes from the request and is included in the signature
const canonical = `${method}:${url}:${targetResourceId}`;
const signature = crypto.createHmac('sha256', context.params.appSecrets.hmac)
.update(canonical)
.digest('hex');
// The attacker can set url to an internal address and targetResourceId to influence the signature
const response = await axios({
url,
method,
data: body,
headers: { 'X-Signature': signature, 'X-Resource-ID': targetResourceId }
});
return response.data;
}
}
});
Remediated pattern: strict destination allowlist and signing only safe fields
const crypto = require('crypto');
const axios = require('axios');
// Safe: destination is controlled by server logic, not user input
app.service('proxy').hooks({
before: {
async create(data, context) {
const { method = 'GET', body, targetResourceId } = data;
// Validate and restrict the destination to known internal services
const allowedEndpoints = new Set([
'https://internal-api.example.com/v1/resource',
'https://internal-api.example.com/v1/other'
]);
const baseUrl = 'https://internal-api.example.com/v1/resource';
if (!allowedEndpoints.has(baseUrl)) {
throw new Error('Unsupported endpoint');
}
// Exclude destination info from the signed canonical
const canonical = `${method}:${baseUrl}:${targetResourceId}`;
const signature = crypto.createHmac('sha256', context.params.appSecrets.hmac)
.update(canonical)
.digest('hex');
// Use a fixed destination; never forward user-supplied URLs
const response = await axios({
url: baseUrl,
method,
data: body,
headers: { 'X-Signature': signature, 'X-Resource-ID': targetResourceId }
});
return response.data;
}
}
});
Additional hardening steps
- Validate and normalize destinations: Use a strict allowlist of hosts and paths. Reject any user-supplied URLs or paths that point to non-allowlisted locations.
- Exclude routing data from signatures: Do not include the request URL, host, port, or path in the Hmac canonical string. Sign only the application-level payload fields that must be protected.
- Enforce timeouts and network policies: Configure HTTP client timeouts and, where possible, restrict egress to known IP ranges or service meshes to reduce the impact of SSRF.
- Audit signature scope: Regularly review which fields are included in the canonical string and ensure sensitive inputs (such as URLs, redirect targets, or host overrides) are not part of it.
By decoupling the destination from the signed data and validating the target explicitly, FeathersJS services can use Hmac Signatures for integrity without introducing SSRF. middleBrick can help identify whether user-controlled inputs participate in signature material or influence the request target, providing prioritized findings and remediation guidance.
Related CWEs: ssrf
| CWE ID | Name | Severity |
|---|---|---|
| CWE-918 | Server-Side Request Forgery (SSRF) | CRITICAL |
| CWE-441 | Unintended Proxy or Intermediary (Confused Deputy) | HIGH |