mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
143 lines
4.6 KiB
TypeScript
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);
|
|
}
|
|
} |