HIGH jwt misconfigurationfeathersjs

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 jwt

The 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 IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

How can I tell if my Feathersjs JWT secret is too weak?
A weak JWT secret in Feathersjs is typically under 32 characters or uses common words/patterns. Run 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?
No, Feathersjs's default JWT configuration doesn't enforce expiration validation unless you explicitly set 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.