Logging Monitoring Failures with Jwt Tokens
How Logging Monitoring Failures Manifests in Jwt Tokens
Logging monitoring failures in JWT token implementations create dangerous blind spots that attackers exploit to maintain persistent access. The most critical manifestation occurs when JWT verification failures go unlogged, allowing attackers to brute-force token signatures without detection. Consider a typical Express.js middleware that silently drops invalid tokens:
const verifyToken = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({error: 'No token'});
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
// Silent failure - no logging, no monitoring
return res.status(401).json({error: 'Invalid token'});
}
};
This pattern enables attackers to test thousands of tokens per minute without triggering any alerts. The absence of rate limiting on verification attempts compounds the problem, as does the lack of monitoring for failed verification patterns.
Another critical failure point appears in token refresh mechanisms. Many implementations log successful token generation but fail to monitor for suspicious refresh patterns:
app.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;
// No monitoring for refresh token usage patterns
// No logging of refresh token rotation frequency
jwt.verify(refreshToken, process.env.REFRESH_SECRET, (err, user) => {
if (err) return res.status(401).json({error: 'Invalid refresh'});
const newToken = jwt.sign({userId: user.id}, process.env.JWT_SECRET, {
expiresIn: '15m'
});
res.json({token: newToken});
});
});
Attackers exploit this by rotating refresh tokens rapidly, maintaining persistent access while the system remains unaware of the abnormal refresh rate.
Token revocation logging failures represent another critical vulnerability. When JWTs are blacklisted for security reasons, the lack of monitoring means attackers can continue using revoked tokens:
const blacklist = new Set();
app.use((req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (blacklist.has(token)) {
// No logging of revoked token usage attempt
return res.status(401).json({error: 'Token revoked'});
}
next();
});
Without logging these revocation attempts, security teams cannot detect when attackers are using stolen or previously compromised tokens.
Jwt Tokens-Specific Detection
Detecting logging monitoring failures in JWT implementations requires systematic scanning of both code patterns and runtime behavior. The most effective approach combines static analysis with active black-box testing.
Static code analysis should search for these anti-patterns:
const antiPatterns = [
// Silent catch blocks
/catch\s*\(\s*err\s*\)\s*\{\s*[^}]*return[^}]*}/,
// Missing logging on verification failures
/jwt\.verify\s*\(\s*[^,]*,\s*[^,]*,\s*\(\s*err\s*,\s*[^)]*\)\s*\{\s*if\s*\(\s*err\s*\)/,
// No rate limiting on verification endpoints
/\/verify\s*[^}]*\{\s*(?!.*rateLimit)/,
// Missing refresh token monitoring
/\/refresh\s*[^}]*\{\s*(?!.*monitor|track|log)/,
// No revocation logging
/blacklist\.has\s*\(\s*[^)]*\)\s*\{\s*(?!.*log|console\.error)/
];
Runtime detection requires active probing of JWT endpoints to identify monitoring gaps. This involves:
- Sending malformed tokens to verify if verification failures are logged
- Rapidly refreshing valid tokens to detect rate limiting and monitoring
- Using revoked tokens to check for revocation logging
- Testing for timing attacks that reveal information about token validation
middleBrick's JWT-specific scanning identifies these monitoring failures through black-box testing. The scanner attempts to verify thousands of invalid tokens to determine if the system logs these attempts. It also tests refresh token endpoints for monitoring capabilities and checks if revoked tokens trigger any security alerts.
The scanner specifically looks for:
| Check Type | Detection Method | Risk Level |
|---|---|---|
| Silent Verification Failures | Send invalid tokens, check for logs/monitoring | High |
| Refresh Token Abuse | Rapid refresh attempts, monitor response patterns | High |
| Revocation Bypass | Use revoked tokens, check for detection | Critical |
| Rate Limiting Absence | Flood verification endpoint, measure response | High |
middleBrick's GitHub Action integration can automatically fail CI/CD builds when logging monitoring failures are detected, preventing vulnerable JWT implementations from reaching production.
Jwt Tokens-Specific Remediation
Remediating logging monitoring failures in JWT implementations requires comprehensive instrumentation and monitoring across all token lifecycle stages. The following code demonstrates proper logging and monitoring patterns:
1. Enhanced JWT Verification with Comprehensive Logging:
const jwt = require('jsonwebtoken');
const winston = require('winston');
const logger = winston.createLogger({
transports: [
new winston.transports.File({ filename: 'jwt-security.log' }),
new winston.transports.Console()
]
});
const verifyToken = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
logger.warn('Missing JWT token', { ip: req.ip, endpoint: req.originalUrl });
return res.status(401).json({error: 'No token'});
}
try {
const start = Date.now();
const decoded = await jwt.verify(token, process.env.JWT_SECRET);
const duration = Date.now() - start;
// Monitor verification timing for potential timing attacks
if (duration > 100) {
logger.warn('Slow JWT verification', { duration, ip: req.ip });
}
req.user = decoded;
next();
} catch (err) {
// Comprehensive logging of verification failures
const failureType = err.name || 'UnknownError';
logger.error('JWT verification failed', {
type: failureType,
ip: req.ip,
endpoint: req.originalUrl,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString()
});
// Rate limiting for verification attempts
await rateLimitVerification(req.ip);
return res.status(401).json({error: 'Invalid token'});
}
};
2. Refresh Token Monitoring with Anomaly Detection:
const refreshHistory = new Map();
app.post('/refresh', async (req, res) => {
const refreshToken = req.cookies.refreshToken;
const ip = req.ip;
// Track refresh token usage patterns
const history = refreshHistory.get(refreshToken) || [];
history.push(Date.now());
refreshHistory.set(refreshToken, history.slice(-100)); // Keep last 100 attempts
// Detect abnormal refresh patterns
const recentAttempts = history.filter(ts => ts > Date.now() - 60000);
if (recentAttempts.length > 10) {
logger.warn('Suspicious refresh token activity', {
attempts: recentAttempts.length,
ip,
refreshToken: refreshToken.substring(0, 20) + '...'
});
// Trigger security alert
await triggerSecurityAlert({
type: 'refresh-abuse',
details: { attempts: recentAttempts.length, ip }
});
}
try {
const decoded = await jwt.verify(refreshToken, process.env.REFRESH_SECRET);
// Generate new token with proper logging
const newToken = jwt.sign({userId: decoded.id}, process.env.JWT_SECRET, {
expiresIn: '15m'
});
logger.info('Refresh token successfully used', {
userId: decoded.id,
ip,
newTokenDuration: '15m'
});
res.json({token: newToken});
} catch (err) {
logger.error('Refresh token verification failed', {
type: err.name,
ip,
userAgent: req.headers['user-agent']
});
return res.status(401).json({error: 'Invalid refresh'});
}
});
3. Revocation Monitoring with Real-time Alerts:
const revokedTokens = new Set();
const revocationLog = [];
app.use((req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (revokedTokens.has(token)) {
// Log revoked token usage attempt
const attempt = {
token: token.substring(0, 20) + '...',
ip: req.ip,
endpoint: req.originalUrl,
timestamp: new Date().toISOString(),
userAgent: req.headers['user-agent']
};
revocationLog.push(attempt);
logger.warn('Revoked token detected', attempt);
// Trigger immediate security alert
triggerSecurityAlert({
type: 'revoked-token-usage',
details: attempt
});
return res.status(401).json({error: 'Token revoked'});
}
next();
});
// Revocation logging endpoint for security team
app.post('/api/jwt/revoke', async (req, res) => {
const { token, reason, userId } = req.body;
revokedTokens.add(token);
// Log revocation with full context
logger.info('JWT token revoked', {
token: token.substring(0, 20) + '...',
reason,
userId,
timestamp: new Date().toISOString(),
actor: req.user?.id || 'system'
});
res.json({success: true});
});
4. Centralized JWT Security Monitoring:
// Centralized monitoring service
class JWTMonitor {
constructor() {
this.stats = {
verificationAttempts: 0,
verificationFailures: 0,
refreshAttempts: 0,
revokedAttempts: 0,
anomalies: 0
};
}
trackVerification(success, ip, endpoint) {
this.stats.verificationAttempts++;
if (!success) this.stats.verificationFailures++;
// Detect unusual patterns
if (this.stats.verificationAttempts % 1000 === 0) {
const failureRate = this.stats.verificationFailures / this.stats.verificationAttempts;
if (failureRate > 0.1) { // 10% failure rate
logger.warn('High JWT verification failure rate', {
failureRate,
totalAttempts: this.stats.verificationAttempts,
failureCount: this.stats.verificationFailures
});
}
}
}
trackRefreshAttempt(ip, success) {
this.stats.refreshAttempts++;
if (!success) {
this.stats.anomalies++;
if (this.stats.anomalies > 50) {
logger.warn('Suspicious refresh token activity detected', {
totalAnomalies: this.stats.anomalies,
recentAttempts: this.stats.refreshAttempts
});
}
}
}
trackRevocationAttempt(ip, endpoint) {
this.stats.revokedAttempts++;
if (this.stats.revokedAttempts > 10) {
logger.warn('Multiple revoked token attempts', {
attempts: this.stats.revokedAttempts,
ip,
endpoint
});
}
}
}
const jwtMonitor = new JWTMonitor();