import rateLimit from 'express-rate-limit'; // CSRF defense-in-depth: session cookie is SameSite=Strict, and every // state-changing request must additionally come from our own origin. export function csrfOriginCheck(req, res, next) { if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) return next(); const origin = req.headers.origin || ''; const host = req.headers['x-forwarded-host'] || req.headers.host || ''; if (origin) { let originHost; try { originHost = new URL(origin).host; } catch { return res.status(403).json({ error: 'Origine invalide' }); } if (originHost !== host) { return res.status(403).json({ error: 'Origine non autorisée' }); } } next(); } export const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, limit: 10, standardHeaders: true, legacyHeaders: false, message: { error: 'Trop de tentatives de connexion. Réessayez dans 15 minutes.' }, }); export const publicFormLimiter = rateLimit({ windowMs: 60 * 60 * 1000, limit: 20, standardHeaders: true, legacyHeaders: false, message: { error: 'Trop de requêtes. Réessayez plus tard.' }, }); export const globalLimiter = rateLimit({ windowMs: 60 * 1000, limit: 300, standardHeaders: true, legacyHeaders: false, message: { error: 'Trop de requêtes.' }, });