Jwt Misconfiguration in Feathersjs with Api Keys
Jwt Misconfiguration in Feathersjs with Api Keys — how this specific combination creates or exposes the vulnerability
FeathersJS is a JavaScript framework for real-time applications that commonly uses JWT for stateless authentication. When JWT is combined with API keys, misconfigurations can expose both token validation flaws and key-management weaknesses. A typical vulnerability arises when the API key is treated as a bearer token or is embedded in JWT claims without proper validation boundaries, enabling confused-deployments or token substitution attacks.
One realistic misconfiguration is accepting an API key in the Authorization header as Bearer {apiKey} while the server logic mistakenly skips signature verification, or reuses a public key/secret across environments. If JWT verification is incomplete (for example, not validating iss, aud, or exp), an attacker who obtains or guesses a weak API key can forge tokens with elevated scopes or impersonate other services. Another scenario involves middleware ordering: if API key checks are placed after JWT extraction, an attacker can send a malformed JWT that bypasses intended authorization logic because the framework does not enforce strict fail-closed behavior.
Insecure default configurations in FeathersJS templates or plugins can also contribute. For instance, generating JWTs with weak algorithms (e.g., none or HS256 with a predictable secret) and allowing API keys to be passed in query parameters or headers without strict allowlists increases risk. Attack patterns include token replay, privilege escalation via modified JWT claims, and lateral movement when API keys are over-permissioned. These issues map to OWASP API Top 10 controls such as broken object level authorization and insufficient logging/monitoring, and may be reflected in compliance scopes like SOC2 and PCI-DSS when authentication is involved.
During a black-box scan, such misconfigurations can be detected through unauthenticated probes that inspect authentication bypass paths, analyze JWT structure, and validate how API keys are enforced in protected endpoints. Proper remediation requires aligning token validation, key handling, and middleware sequencing so that each credential type is verified independently and failures default to denial of access.
Api Keys-Specific Remediation in Feathersjs — concrete code fixes
Remediation focuses on strict validation, separation of concerns, and defense-in-depth for API keys used alongside JWT. Always enforce algorithm restrictions, validate standard JWT claims, and ensure API keys are checked before token processing where appropriate. Below are concrete, syntactically correct examples for FeathersJS that demonstrate secure patterns.
1. Enforce HS256 with a strong secret and validate JWT claims
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
const jwt = require('@feathersjs/authentication-jwt');
const app = express(feathers());
app.configure(jwt({
secret: process.env.JWT_SECRET,
algorithms: ['HS256'],
// Validate standard claims to prevent token misuse
verifyOptions: {
issuer: 'myapi.example.com',
audience: 'myapp-client',
clockTolerance: 60
}
}));
// Ensure authentication middleware is configured to fail closed
app.use('/', app.authenticate('jwt'));
2. Validate API keys separately before JWT processing
const ApiKeyModel = require('./models/api-keys');
// Custom hook to verify API key in a dedicated header
function verifyApiKey(options = {}) {
return async context => {
const apiKey = context.params.headers['x-api-key'];
if (!apiKey) {
throw new Error('Unauthorized: Missing API key');
}
const keyRecord = await ApiKeyModel.findById(apiKey);
if (!keyRecord || keyRecord.revoked) {
throw new Error('Unauthorized: Invalid API key');
}
// Attach resolved key metadata for downstream use, do not treat as auth token
context.params.apiKeyMeta = { id: keyRecord.id, scopes: keyRecord.scopes };
return context;
};
}
// Apply as a global hook or on specific services
app.hooks({
before: {
all: [verifyApiKey()]
}
});
3. Prevent header confusion and restrict allowed transports
// Reject requests that mix API key with Bearer JWT when not expected
function strictTransportPolicy(context) {
const authHeader = context.params.headers.authorization || '';
const hasApiKey = 'x-api-key' in context.params.headers;
// Example policy: API keys must use x-api-key, JWT must use Authorization: Bearer
if (authHeader.startsWith('Bearer ') && hasApiKey) {
throw new Error('Unauthorized: Conflicting authentication headers');
}
if (authHeader.startsWith('Basic ')) {
throw new Error('Unauthorized: Unsupported authentication scheme');
}
return context;
}
app.hooks({
before: {
all: [strictTransportPolicy]
}
});
4. Use environment-managed secrets and avoid query parameters
Never pass API keys or JWT secrets via query strings or logs. Configure secrets through environment variables and sanitize inputs:
// Good practice: load from secure env, validate length
const jwtSecret = process.env.JWT_SECRET;
if (!jwtSecret || Buffer.byteLength(jwtSecret, 'base64') < 32) {
throw new Error('Invalid JWT secret configuration');
}
// In service hooks, avoid logging sensitive headers
app.hooks({
before: {
all: [context => {
delete context.params.headers['x-api-key'];
return context;
}]
}
});
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |