Dangling Dns in Feathersjs with Hmac Signatures
Dangling Dns in Feathersjs with Hmac Signatures — how this specific combination creates or exposes the vulnerability
A Dangling DNS condition occurs when a hostname resolves, but the target application or service is not correctly configured to handle that resolution in a secure, application-aware context. In a Feathersjs service that uses Hmac Signatures for request authentication, this combination can expose an API to unauthorized routing or data leakage if service endpoints are dynamically derived from or influenced by DNS records.
Feathersjs is a framework for real-time web applications and REST/GraphQL APIs. When you add Hmac-based authentication, you typically compute a signature from request properties (method, path, timestamp, body) using a shared secret. The server then recomputes the signature and compares it to the client-provided value. If the service routes or selects backends based on DNS lookups (for example, multi-tenant routing or service discovery), an attacker who can influence the DNS resolution (e.g., via cache poisoning or a compromised DNS record) may cause the client’s request to be sent to a malicious or unintended host that also presents a valid Hmac signature scheme but is not the intended service.
The vulnerability arises specifically because Hmac Signatures in Feathersjs usually cover the request path and method, but may not tightly bind to the resolved network endpoint or the canonical hostname used by the service. If the application uses the Host header or a hostname-derived tenant identifier to select a backend or to validate the signature scope, an attacker with DNS manipulation capabilities can make the request resolve to a server they control. That server can then participate in the Hmac flow (since the shared secret may be configured across related services), leading to unauthorized access or data exposure across what should be isolated endpoints.
For example, consider a Feathersjs deployment where each tenant has a subdomain (tenant1.example.com, tenant2.example.com), and routing is performed via DNS CNAME records. If the Hmac signature is computed over a path like /users/{id} without including the full hostname or a strict tenant identifier, an attacker who can cause tenant1.example.com to resolve to a rogue server under their control could invoke tenant-specific endpoints while presenting valid Hmac signatures accepted by the legitimate backend logic. This is a Dangling DNS issue because the security boundary depends on DNS correctness, but the application does not enforce hostname integrity as part of the authentication check.
To detect such issues, scanning tools perform OpenAPI/Swagger spec analysis alongside runtime checks, correlating endpoint definitions, authentication schemes, and observed DNS behavior. They test whether changing the resolved host while preserving a valid Hmac signature leads to authorization bypass or data exposure, mapping findings to frameworks like the OWASP API Top 10.
Hmac Signatures-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on ensuring that Hmac signatures in Feathersjs bind the signature to the exact service endpoint and hostname, preventing resolution-based bypasses. You should include the request hostname (or a canonical service identifier) as part of the signed payload and enforce strict hostname verification on the server.
Example: Secured Hmac Signature with Host Binding
The following example shows a client that includes the hostname in the signed string and a server that validates both the signature and the expected hostname.
// Client: Compute Hmac over method, path, timestamp, hostname, and body
const crypto = require('crypto');
function signRequest({ method, path, hostname, timestamp, body, sharedSecret }) {
const payload = [
method.toUpperCase(),
path,
hostname,
timestamp,
typeof body === 'string' ? body : JSON.stringify(body)
].join('\n');
return crypto.createHmac('sha256', sharedSecret).update(payload).digest('hex');
}
const method = 'GET';
const path = '/users/123';
const hostname = 'api.example.com'; // must match the expected service hostname
const timestamp = Date.now().toString();
const body = {};
const sharedSecret = 'super-secret-shared';
const signature = signRequest({ method, path, hostname, timestamp, body, sharedSecret });
// Include signature and hostname in headers
fetch(`https://${hostname}${path}`, {
method,
headers: {
'X-Auth-Timestamp': timestamp,
'X-Auth-Signature': signature,
'X-Expected-Hostname': hostname
},
body: JSON.stringify(body)
});
// Server: Verify signature and enforce canonical hostname in Feathersjs hook
const crypto = require('crypto');
function verifyHmacSignature(req) {
const sharedSecret = process.env.SHARED_SECRET;
const expectedHostname = 'api.example.com'; // canonical hostname
const providedHostname = req.headers['x-expected-hostname'];
const timestamp = req.headers['x-auth-timestamp'];
const providedSignature = req.headers['x-auth-signature'];
// Basic time skew protection
const now = Date.now();
const reqTime = Number(timestamp);
if (Math.abs(now - reqTime) > 30000) {
throw new Error('Request expired');
}
// Enforce canonical hostname
if (providedHostname !== expectedHostname) {
throw new Error('Invalid hostname');
}
const payload = [
req.method.toUpperCase(),
req.path,
providedHostname,
timestamp,
typeof req.body === 'string' ? req.body : JSON.stringify(req.body)
].join('\n');
const expected = crypto.createHmac('sha256', sharedSecret).update(payload).digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(providedSignature), Buffer.from(expected))) {
throw new Error('Invalid signature');
}
return true;
}
// Feathersjs before hook
module.exports = function () {
return function (context) {
verifyHmacSignature(context.params.raw || context.params.req || {});
return context;
};
};
Key practices:
- Include the canonical hostname (or a strict tenant identifier) as part of the signed payload.
- Do not rely solely on DNS-based routing to determine the signing scope; bind authentication to the expected hostname explicitly.
- Validate the hostname on the server even if your service is behind a proxy; reject requests where the provided hostname does not match the service’s canonical name.
- Use constant-time comparison for signatures to avoid timing attacks.
These steps reduce the risk that a DNS manipulation or misconfiguration will allow an attacker to reuse valid Hmac signatures across unintended endpoints.