Files
LogiFlow/server/security.ts
T
2025-08-11 14:43:48 +00:00

143 lines
4.6 KiB
TypeScript

import { Express, Request, Response, NextFunction } from 'express';
import rateLimit from 'express-rate-limit';
// Headers de sécurité
export function setupSecurityHeaders(app: Express) {
app.use((req: Request, res: Response, next: NextFunction) => {
// Protection contre les attaques XSS
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
// Protection HTTPS
if (process.env.NODE_ENV === 'production') {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
}
// Politique de sécurité du contenu
res.setHeader('Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: https:; " +
"connect-src 'self' ws: wss:; " +
"font-src 'self' data:;"
);
// Protection contre les attaques de référence
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
// Protection des données sensibles
res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
next();
});
}
// Limitation du taux de requêtes
export function setupRateLimiting(app: Express) {
// Limiteur général
const generalLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 1000, // limite chaque IP à 1000 requêtes par fenêtre
message: {
error: 'Trop de requêtes depuis cette IP, veuillez réessayer plus tard.',
},
standardHeaders: true,
legacyHeaders: false,
trustProxy: 1, // Configuration sécurisée pour Docker (1 proxy de confiance)
skip: (req) => {
// Skip rate limiting for health checks
return req.path === '/api/health';
}
});
// Limiteur pour l'authentification
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limite les tentatives de connexion
message: {
error: 'Trop de tentatives de connexion, veuillez réessayer plus tard.',
},
standardHeaders: true,
legacyHeaders: false,
trustProxy: 1, // Configuration sécurisée pour Docker (1 proxy de confiance)
});
// Limiteur pour l'API - adapté pour une utilisation normale
const apiLimiter = rateLimit({
windowMs: 1 * 60 * 1000, // 1 minute
max: process.env.NODE_ENV === 'development' ? 500 : 300, // 500 en dev, 300 en prod
message: {
error: 'Limite API atteinte, veuillez ralentir vos requêtes.',
},
standardHeaders: true,
legacyHeaders: false,
trustProxy: 1, // Configuration sécurisée pour Docker (1 proxy de confiance)
// Exclure certaines routes critiques du rate limiting strict
skip: (req) => {
return req.path === '/api/health' || req.path === '/api/user';
},
handler: (req, res) => {
console.warn(`🚨 Rate limit reached for IP: ${req.ip} on path: ${req.path} at ${new Date().toISOString()}`);
res.status(429).json({
error: 'Limite API atteinte, veuillez ralentir vos requêtes.',
});
}
});
app.use(generalLimiter);
app.use('/api/login', authLimiter);
app.use('/api/', apiLimiter);
}
// Validation et nettoyage des entrées
export function sanitizeInput(input: any): any {
if (typeof input === 'string') {
// Supprimer les caractères dangereux
return input.replace(/[<>]/g, '').trim();
}
if (typeof input === 'object' && input !== null) {
const sanitized: any = {};
for (const key in input) {
sanitized[key] = sanitizeInput(input[key]);
}
return sanitized;
}
return input;
}
// Middleware de nettoyage des requêtes
export function setupInputSanitization(app: Express) {
app.use((req: Request, res: Response, next: NextFunction) => {
if (req.body) {
req.body = sanitizeInput(req.body);
}
if (req.query) {
req.query = sanitizeInput(req.query);
}
if (req.params) {
req.params = sanitizeInput(req.params);
}
next();
});
}
// Middleware de logging sécurisé
export function secureLog(message: string, data?: any) {
const timestamp = new Date().toISOString();
const logData = data ? JSON.stringify(data, null, 2) : '';
// En production, ne pas logger les données sensibles
if (process.env.NODE_ENV === 'production') {
if (message.includes('password') || message.includes('token')) {
console.log(`[${timestamp}] ${message} - [SENSITIVE DATA HIDDEN]`);
} else {
console.log(`[${timestamp}] ${message}`, logData);
}
} else {
console.log(`[${timestamp}] ${message}`, logData);
}
}