Man In The Middle in Feathersjs with Api Keys
Man In The Middle in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
A Man In The Middle (MitM) scenario in a FeathersJS application using API keys occurs when an attacker can intercept or tamper with traffic between the client and the FeathersJS server. If API keys are transmitted in an unauthenticated or weakly protected channel, an interceptor can capture, replay, or modify them to impersonate clients or escalate privileges.
FeathersJS is event-driven and typically exposes services over HTTP or WebSocket transports. When API keys are passed in headers (e.g., Authorization: ApiKey <key>) over unencrypted HTTP, or when TLS is terminated incorrectly (e.g., mixed content or weak ciphers), the keys can be observed or altered in transit. Similarly, if the client stores API keys insecurely (localStorage or non-HTTPS cookies), an attacker with network-level access can harvest them and inject themselves into the session.
In Feathers, a service hook that validates API keys can be bypassed if the request path is intercepted and the key is replaced with a known value before reaching the server. For example, consider a hook that checks hook.params.headers['x-api-key']; an attacker who modifies the header mid-path can substitute a stolen key, and the hook may accept it as valid. This is especially risky when the API key has broad permissions (e.g., administrative scope) and is transmitted without additional binding to the session or client context.
Additionally, WebSocket-based transports in Feathers can be vulnerable if the initial HTTP handshake carrying the API key is downgraded or intercepted. An attacker who can perform DNS spoofing or ARP poisoning on the local network can redirect traffic to a rogue server that mimics the Feathers endpoint, harvesting keys and injecting malicious events or data exfiltration requests.
Real-world attack patterns that map to this include OWASP API Top 10 2023 A05:2023 — Security Misconfiguration (e.g., missing transport encryption) and A02:2023 — Cryptographic Failures. Instances of SSRF combined with weak ingress controls can also expose internal endpoints that rely on API keys without mTLS or request binding.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on ensuring API keys are never exposed in cleartext, are validated with strong server-side checks, and are bound to client context where possible. Always enforce HTTPS for all endpoints, avoid storing keys in insecure client-side storage, and design hooks to validate keys within a broader request integrity check.
Below are concrete FeathersJS code examples that demonstrate secure handling of API keys.
1. Enforce HTTPS and secure transport
Ensure your Feathers server only accepts secure connections and rejects HTTP. Use middleware or infrastructure-level redirects to upgrade all traffic to TLS.
// server.js (secure transport enforcement)
const feathers = require('@feathersjs/feathers');
const express = require('@feathsjs/express');
const app = express(feathers());
// Reject non-HTTPS requests at the edge in production.
// Use a reverse proxy (e.g., Nginx, Load Balancer) to enforce TLS.
app.use((req, res, next) => {
if (req.headers['x-forwarded-proto'] !== 'https') {
return res.status(403).json({ message: 'HTTPS required' });
}
next();
});
2. Validate API keys in a service hook with context binding
Use a before hook to validate the API key and bind it to the request context, making it harder to replay in a different session or IP.
// src/hooks/validate-api-key.js
const crypto = require('crypto');
// Example key-to-client mapping stored securely (e.g., hashed in DB)
const validKeys = {
'abc123': { scope: 'read-write', clientId: 'web-app-01', fingerprint: 'sha256:abcd...' },
'def456': { scope: 'read-only', clientId: 'mobile-app-02', fingerprint: 'sha256:ef01...' }
};
module.exports = function apiKeyHook(options = {}) {
return async context => {
const { headers } = context.params;
const key = headers['x-api-key'] || headers['authorization']?.replace('ApiKey ', '');
if (!key) {
throw new Error('Unauthorized: API key missing');
}
const entry = validKeys[key];
if (!entry) {
throw new Error('Unauthorized: Invalid API key');
}
// Optional: bind key to client IP/user-agent to mitigate MitM
const clientFingerprint = crypto
.createHash('sha256')
.update((context.params.headers['x-forwarded-for'] || context.params.ip) + (headers['user-agent'] || ''))
.digest('hex');
if (!clientFingerprint.includes(entry.fingerprint.slice(-8))) {
throw new Error('Unauthorized: Request context mismatch');
}
// Attach validated key metadata to context for downstream services
context.params.apiKey = { scope: entry.scope, clientId: entry.clientId };
return context;
};
};
3. Apply the hook globally or per-service
Register the hook in your Feathers app to protect all services or selectively for sensitive endpoints.
// src/app.js
const validateApiKey = require('./hooks/validate-api-key');
app.configure(express.rest());
// Apply globally to all services
app.hooks({
before: {
all: [validateApiKey()]
}
});
// Or apply to a specific service
app.use('/admin', adminService);
app.service('admin').hooks({
before: {
all: [validateApiKey()]
}
});
4. Rotate keys and avoid leakage in logs
Ensure API keys are rotated periodically and never logged. Configure transports and error handlers to scrub key headers from output.
// src/hooks/scrub-headers.js
module.exports = function scrubHeadersHook() {
return context => {
const headers = context.params.headers || {};
if (headers['authorization']) {
// Remove sensitive headers from logs
delete context.params.headers['authorization'];
}
if (headers['x-api-key']) {
delete context.params.headers['x-api-key'];
}
return context;
};
};