const logger = require('../utils/logger'); // HTTPS enforcement middleware const enforceHTTPS = (req, res, next) => { // Skip HTTPS enforcement in development if ( process.env.NODE_ENV === "dev" || process.env.NODE_ENV === "development" ) { return next(); } // Check if request is already HTTPS const isSecure = req.secure || req.headers["x-forwarded-proto"] === "https" || req.protocol === "https"; if (!isSecure) { // Use configured allowed host to prevent Host Header Injection const allowedHost = process.env.FRONTEND_URL; // Log the redirect for monitoring if (req.headers.host !== allowedHost) { const reqLogger = logger.withRequestId(req.id); reqLogger.warn("Host header mismatch during HTTPS redirect", { requestHost: req.headers.host, allowedHost, ip: req.ip, url: req.url, eventType: 'SECURITY_HOST_MISMATCH' }); } // Redirect to HTTPS with validated host return res.redirect(301, `https://${allowedHost}${req.url}`); } // Set Strict-Transport-Security header res.setHeader( "Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload" ); next(); }; // Security headers middleware const securityHeaders = (req, res, next) => { // X-Content-Type-Options res.setHeader("X-Content-Type-Options", "nosniff"); // X-Frame-Options res.setHeader("X-Frame-Options", "DENY"); // Referrer-Policy res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin"); // Permissions-Policy (formerly Feature-Policy) res.setHeader( "Permissions-Policy", "camera=(), microphone=(), geolocation=(self)" ); next(); }; // Request ID middleware for tracking const requestId = require("crypto"); const addRequestId = (req, res, next) => { req.id = requestId.randomBytes(16).toString("hex"); res.setHeader("X-Request-ID", req.id); next(); }; // Log security events const logSecurityEvent = (eventType, details, req) => { const reqLogger = logger.withRequestId(req.id || "unknown"); const logEntry = { eventType, ip: req.ip || req.connection.remoteAddress, userAgent: req.get("user-agent"), userId: req.user?.id || "anonymous", ...details, }; reqLogger.warn(`Security event: ${eventType}`, logEntry); }; // Sanitize error messages to prevent information leakage const sanitizeError = (err, req, res, next) => { // Send sanitized error to client const isDevelopment = process.env.NODE_ENV === "dev" || process.env.NODE_ENV === "development"; if (err.status === 400) { // Client errors can be more specific return res.status(400).json({ error: err.message || "Bad Request", requestId: req.id, }); } else if (err.status === 401) { return res.status(401).json({ error: "Unauthorized", requestId: req.id, }); } else if (err.status === 403) { return res.status(403).json({ error: "Forbidden", requestId: req.id, }); } else if (err.status === 404) { return res.status(404).json({ error: "Not Found", requestId: req.id, }); } else { // Server errors should be generic in production return res.status(err.status || 500).json({ error: isDevelopment ? err.message : "Internal Server Error", requestId: req.id, ...(isDevelopment && { stack: err.stack }), }); } }; module.exports = { enforceHTTPS, securityHeaders, addRequestId, logSecurityEvent, sanitizeError, };