import helmet from 'helmet'; import rateLimit from 'express-rate-limit'; import { Request, Response, NextFunction } from 'express'; import { config } from './config'; import { securityLogger } from './logger'; // Security headers middleware export const securityHeaders = helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "blob:"], connectSrc: ["'self'"], fontSrc: ["'self'"], objectSrc: ["'none'"], mediaSrc: ["'self'"], frameSrc: ["'none'"], }, }, crossOriginEmbedderPolicy: false, // Req. for Socket.io hsts: { maxAge: 31536000, includeSubDomains: true, preload: true } }); // Rate limiting export const createRateLimit = rateLimit({ windowMs: config.rateLimit.windowMs, max: config.rateLimit.maxRequests, message: { error: 'Too many requests from this IP, please try again later.', retryAfter: Math.ceil(config.rateLimit.windowMs / 1000) }, standardHeaders: true, legacyHeaders: false, handler: (req: Request, res: Response) => { securityLogger.rateLimitExceeded( req.ip || req.connection.remoteAddress || 'unknown', req.path ); res.status(429).json({ error: 'Too many requests from this IP, please try again later.', retryAfter: Math.ceil(config.rateLimit.windowMs / 1000) }); } }); // Request logging export const requestLogger = (req: Request, res: Response, next: NextFunction) => { next(); }; // Sanitize input export const sanitizeInput = (req: Request, res: Response, next: NextFunction) => { // Recursively sanitize string values const sanitizeValue = (value: any): any => { if (typeof value === 'string') { return value .replace(/)<[^<]*)*<\/script>/gi, '') // Remove script tags .replace(/<[^>]*>/g, '') // Remove HTML tags .trim(); } if (Array.isArray(value)) { return value.map(sanitizeValue); } if (value && typeof value === 'object') { const sanitized: any = {}; for (const [key, val] of Object.entries(value)) { sanitized[key] = sanitizeValue(val); } return sanitized; } return value; }; req.body = sanitizeValue(req.body); req.query = sanitizeValue(req.query); req.params = sanitizeValue(req.params); next(); };