Files
picpeak/backend/src/utils/rateLimitSecurity.js
T
paul f38014099e
Test and Lint / backend-test (push) Successful in 1m12s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m16s
Version and Release / version-bump (push) Successful in 35s
Version and Release / trigger-drone (push) Successful in 3s
fix: show hero image in thumbnail grid on hero gallery layout
2025-07-13 20:03:27 +02:00

119 lines
3.2 KiB
JavaScript

/**
* Rate Limiting Security Utilities
* Provides secure rate limiting that prevents bypass attempts
*/
const jwt = require('jsonwebtoken');
const logger = require('./logger');
/**
* Safely check if a request has a valid admin token
* Used to determine if rate limiting should be skipped
*
* IMPORTANT: This prevents the bypass vulnerability where
* invalid tokens could skip rate limiting
*
* @param {Object} req - Express request object
* @returns {boolean} - True only if token is valid AND admin type
*/
function hasValidAdminToken(req) {
try {
// Only check admin paths
if (!req.path.startsWith('/api/admin/')) {
return false;
}
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return false;
}
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
// Critical: Verify token is valid before skipping rate limit
// This prevents invalid tokens from bypassing rate limiting
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Additional validation
if (!decoded || typeof decoded !== 'object') {
return false;
}
// Must be admin type to skip rate limiting
if (decoded.type !== 'admin') {
logger.warn('Non-admin token attempted to bypass rate limit', {
path: req.path,
tokenType: decoded.type,
ip: req.ip
});
return false;
}
// Optional: Check token age (prevent old tokens)
const tokenAge = Date.now() - (decoded.iat * 1000);
const maxAge = 24 * 60 * 60 * 1000; // 24 hours
if (tokenAge > maxAge) {
logger.warn('Old admin token attempted to bypass rate limit', {
path: req.path,
tokenAge: Math.floor(tokenAge / 1000 / 60) + ' minutes',
ip: req.ip
});
return false;
}
// Valid admin token - can skip rate limiting
return true;
} catch (error) {
// Any error means token is invalid
// Log attempts with invalid tokens (potential attacks)
if (error.name === 'JsonWebTokenError') {
logger.warn('Invalid token attempted to bypass rate limit', {
path: req.path,
error: error.message,
ip: req.ip
});
}
// Apply rate limiting for any invalid token
return false;
}
}
/**
* Create a skip function for rate limiter that prevents bypass
* @returns {Function} Skip function for express-rate-limit
*/
function createSecureSkipFunction() {
return (req) => {
// In development, be more lenient with public settings
if (process.env.NODE_ENV === 'development' && req.path === '/api/public/settings') {
return true;
}
// Only skip for valid admin tokens
return hasValidAdminToken(req);
};
}
/**
* Log rate limit hits for security monitoring
* @param {Object} req - Express request object
* @param {Object} res - Express response object
*/
function logRateLimitHit(req, res) {
logger.warn('Rate limit exceeded', {
ip: req.ip,
path: req.path,
userAgent: req.headers['user-agent'],
remaining: res.getHeader('X-RateLimit-Remaining'),
limit: res.getHeader('X-RateLimit-Limit')
});
}
module.exports = {
hasValidAdminToken,
createSecureSkipFunction,
logRateLimitHit
};