Jwt Misconfiguration in Feathersjs
How Jwt Misconfiguration Manifests in Feathersjs
JWT misconfiguration in Feathersjs applications often occurs through improper token validation, weak signing algorithms, or missing security claims. The most common manifestation is using HS256 with weak secrets or failing to validate the alg header, allowing attackers to substitute RS256 tokens and bypass authentication entirely.
Consider this vulnerable Feathersjs authentication setup:
const authentication = require('@feathersjs/authentication');
const jwt = require('@feathersjs/authentication-jwt');
module.exports = app => {
app.configure(authentication({
secret: 'weak-secret', // Critical vulnerability
jwtOptions: {
expiresIn: '1d'
}
}));
app.configure(jwt());
};The weak secret allows brute-force attacks, while missing algorithm validation enables alg:none attacks. Another common issue is improper token expiration handling:
// Vulnerable: no expiration validation
const { authenticate } = require('@feathersjs/authentication');
async function checkAccess(context) {
const { user } = await authenticate('jwt')(context);
// Missing expiration check
}Feathersjs's default JWT strategy doesn't enforce secure claims by default. Attackers can exploit missing aud (audience) and iss (issuer) validation to use tokens across different services or environments. The framework's flexibility with authentication hooks also creates risks when developers forget to apply authentication middleware to protected routes:
// Vulnerable: missing authentication hook
app.get('/admin', async (req, res) => {
const adminData = await app.service('admin').find();
res.json(adminData); // No auth check!
});Feathersjs-Specific Detection
Detecting JWT misconfigurations in Feathersjs requires examining both configuration files and runtime behavior. Start by inspecting your authentication.js configuration:
# Check for weak secrets and missing security claims
cat config/default.json | grep -A 10 "authentication"Look for secrets shorter than 32 characters, missing alg validation, and absent aud/iss claims. The @feathersjs/authentication-jwt package's default configuration is permissive—you must explicitly add security constraints.
middleBrick's scanner specifically tests Feathersjs JWT endpoints for:
- Algorithm confusion attacks (
alg:none,alg:None) - Weak secret detection through timing analysis
- Missing expiration validation
- Cross-service token replay
- Insufficient entropy in JWT secrets
Run middleBrick against your Feathersjs API:
npm install -g middlebrick
middlebrick scan https://yourapi.com --target jwtThe scanner performs active testing by attempting to authenticate with malformed tokens, checking for information disclosure in authentication errors, and verifying that JWT middleware is properly applied across all routes. It specifically looks for Feathersjs's authenticate hook patterns and tests whether protected endpoints can be accessed without valid tokens.
Code-level detection involves checking your authentication hooks:
// Check all service hooks for authentication
const fs = require('fs');
const services = fs.readdirSync('./services');
services.forEach(service => {
const hooks = require(`./services/${service}/hooks`);
const hasAuth = Object.values(hooks).some(hook =>
hook.name === 'authenticate' || hook.type === 'before'
});Feathersjs-Specific Remediation
Remediate JWT misconfigurations in Feathersjs by strengthening your authentication configuration and implementing proper validation. Start with a robust JWT setup:
const authentication = require('@feathersjs/authentication');
const jwt = require('@feathersjs/authentication-jwt');
const crypto = require('crypto');
module.exports = app => {
const secret = process.env.JWT_SECRET || crypto.randomBytes(64).toString('hex');
if (secret.length < 64) {
throw new Error('JWT_SECRET must be at least 64 characters');
}
app.configure(authentication({
secret,
jwtOptions: {
expiresIn: '2h',
issuer: 'your-app-name',
audience: 'your-app-audience',
algorithms: ['HS256'] // Explicitly allow only HS256
},
authService: 'users'
}));
app.configure(jwt());
};Implement custom validation hooks to prevent algorithm confusion:
const { BadRequest } = require('@feathersjs/errors');
async function validateJwtAlgorithm(context) {
const token = context.params.token;
if (!token) return; // Let auth handle missing tokens
const decoded = jwt.decode(token, { complete: true });
if (!decoded || !decoded.header || decoded.header.alg !== 'HS256') {
throw new BadRequest('Invalid JWT algorithm');
}
return context;
}Secure your Feathersjs services by ensuring all protected routes use authentication hooks:
const { authenticate } = require('@feathersjs/authentication');
module.exports = app => {
const service = app.service('messages');
service.hooks({
before: {
all: [authenticate('jwt')],
find: [checkPermissions],
update: [checkPermissions],
patch: [checkPermissions],
remove: [checkPermissions]
}
});
};Add rate limiting to prevent brute-force attacks on JWT endpoints:
const rateLimit = require('express-rate-limit');
const jwtLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Too many authentication attempts'
});
app.post('/authentication', jwtLimiter, authenticationStrategies);Finally, implement proper token revocation and rotation strategies:
class TokenService {
async revokeToken(userId, tokenId) {
const redis = app.get('redis');
await redis.srem(`user:${userId}:tokens`, tokenId);
}
async isTokenRevoked(userId, tokenId) {
const redis = app.get('redis');
return await redis.sismember(`user:${userId}:tokens`, tokenId);
}
}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 |
Frequently Asked Questions
How can I tell if my Feathersjs JWT secret is too weak?
echo $JWT_SECRET | wc -c to check length. middleBrick's scanner actively tests for weak secrets by attempting to decode tokens with common passwords and analyzing entropy. Strong secrets should be at least 64 characters of random data, stored in environment variables, not hardcoded in configuration files.Does Feathersjs automatically validate JWT expiration?
expiresIn in jwtOptions. Even with expiration set, you need to ensure your authentication hooks properly check the exp claim. middleBrick tests this by using expired tokens and verifying whether your API accepts them. Always include expiration validation and consider adding clock tolerance for time skew between services.