Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | /** * Middleware CSRF — Protection contre les requêtes cross-site forgées. * * Utilise le pattern "double submit cookie" : * 1. Le serveur génère un token aléatoire et l'envoie dans un cookie lisible par JS. * 2. Le frontend lit ce cookie et l'envoie dans le header X-CSRF-Token. * 3. Le serveur vérifie que le header correspond au cookie. * * Combiné avec SameSite=Lax sur les cookies d'auth, cela offre * une double protection contre les attaques CSRF. * * @module middleware/csrf */ const crypto = require('node:crypto'); const CSRF_COOKIE = 'csrf_token'; const CSRF_HEADER = 'x-csrf-token'; // Méthodes HTTP qui ne modifient pas l'état (pas besoin de CSRF) const SAFE_METHODS = new Set(['GET', 'HEAD', 'OPTIONS']); /** * Middleware qui envoie un CSRF token en cookie si absent. * Doit être placé après cookie-parser. */ function csrfToken(req, res, next) { // Si pas de cookie CSRF, en générer un if (!req.cookies[CSRF_COOKIE]) { const token = crypto.randomBytes(32).toString('hex'); const isProduction = process.env.NODE_ENV === 'production'; res.cookie(CSRF_COOKIE, token, { httpOnly: false, // Le JS doit pouvoir le lire secure: isProduction, sameSite: 'Lax', maxAge: 24 * 60 * 60 * 1000, // 24h }); } next(); } /** * Middleware qui valide le token CSRF pour les requêtes mutantes. * Vérifie que le header X-CSRF-Token correspond au cookie csrf_token. */ function csrfProtect(req, res, next) { // Les méthodes sûres n'ont pas besoin de CSRF if (SAFE_METHODS.has(req.method)) { return next(); } const cookieToken = req.cookies[CSRF_COOKIE]; const headerToken = req.headers[CSRF_HEADER]; if (!cookieToken || !headerToken) { return res.status(403).json({ error: 'Token CSRF manquant.' }); } // Comparaison en temps constant pour éviter les timing attacks if (!crypto.timingSafeEqual(Buffer.from(cookieToken), Buffer.from(headerToken))) { return res.status(403).json({ error: 'Token CSRF invalide.' }); } next(); } module.exports = { csrfToken, csrfProtect }; |