All checks were successful
continuous-integration/drone/push Build is passing
Fixed ERR_BLOCKED_BY_RESPONSE.NotSameOrigin errors by implementing proper CORS headers for public resources: - Added Access-Control-Allow-Origin: * for public assets - Changed Cross-Origin-Resource-Policy to cross-origin for CDN files - Maintained strict security for sensitive routes - Added cache control for optimal CDN performance Affected files: - /public/* (images, CSS, JS, fonts) - /cdn-files/* (uploaded files) - /attachments (file serving)
95 lines
3.9 KiB
JavaScript
95 lines
3.9 KiB
JavaScript
/**
|
|
* Middleware de sécurité pour ajouter les headers HTTP sécurisés
|
|
* Conforme aux bonnes pratiques OWASP
|
|
*/
|
|
|
|
const securityHeadersMiddleware = (req, res, next) => {
|
|
// Désactive l'envoi de l'en-tête X-Powered-By pour ne pas révéler la stack technique
|
|
res.removeHeader('X-Powered-By');
|
|
|
|
// Content Security Policy (CSP) - Protège contre les attaques XSS
|
|
res.setHeader(
|
|
'Content-Security-Policy',
|
|
[
|
|
"default-src 'self'",
|
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://code.jquery.com https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com https://cdn.jsdelivr.net https://cdn.tailwindcss.com",
|
|
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://fonts.googleapis.com",
|
|
"img-src 'self' data: https: blob:",
|
|
"font-src 'self' https://cdnjs.cloudflare.com https://fonts.gstatic.com",
|
|
"connect-src 'self' ws: wss: https://cdnjs.cloudflare.com https://maxcdn.bootstrapcdn.com",
|
|
"frame-ancestors 'none'",
|
|
"base-uri 'self'",
|
|
"form-action 'self'"
|
|
].join('; ')
|
|
);
|
|
|
|
// X-Content-Type-Options - Empêche le navigateur de deviner le type MIME
|
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
|
|
// X-Frame-Options - Protège contre le clickjacking
|
|
res.setHeader('X-Frame-Options', 'DENY');
|
|
|
|
// X-XSS-Protection - Active la protection XSS du navigateur
|
|
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
|
|
// Strict-Transport-Security (HSTS) - Force HTTPS
|
|
if (req.secure || process.env.NODE_ENV === 'production') {
|
|
res.setHeader(
|
|
'Strict-Transport-Security',
|
|
'max-age=31536000; includeSubDomains; preload'
|
|
);
|
|
}
|
|
|
|
// Referrer-Policy - Contrôle les informations de référence envoyées
|
|
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
|
|
// Permissions-Policy - Contrôle les fonctionnalités du navigateur
|
|
res.setHeader(
|
|
'Permissions-Policy',
|
|
[
|
|
'camera=()',
|
|
'microphone=()',
|
|
'geolocation=()',
|
|
'payment=()',
|
|
'usb=()',
|
|
'magnetometer=()',
|
|
'accelerometer=()',
|
|
'gyroscope=()'
|
|
].join(', ')
|
|
);
|
|
|
|
// X-Permitted-Cross-Domain-Policies - Restreint les politiques cross-domain
|
|
res.setHeader('X-Permitted-Cross-Domain-Policies', 'none');
|
|
|
|
// Cross-Origin-Embedder-Policy - Désactivé pour permettre les ressources externes (avatars, images CDN)
|
|
// res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
|
|
|
|
// Cross-Origin-Opener-Policy - Isole le contexte de navigation
|
|
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
|
|
|
|
// CORS Headers pour CDN - Permet l'accès cross-origin aux ressources publiques
|
|
const isPublicResource = req.path.startsWith('/public/') ||
|
|
req.path.startsWith('/cdn-files/') ||
|
|
req.path.startsWith('/attachments') ||
|
|
req.path.match(/\.(png|jpg|jpeg|gif|svg|webp|ico|css|js|woff|woff2|ttf|eot)$/i);
|
|
|
|
if (isPublicResource) {
|
|
// Autoriser toutes les origines pour les ressources publiques du CDN
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Accept, Range');
|
|
res.setHeader('Access-Control-Expose-Headers', 'Content-Length, Content-Range, Content-Type');
|
|
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
|
|
|
|
// Cache control pour les ressources statiques
|
|
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
|
} else {
|
|
// Routes sensibles : garder la sécurité stricte
|
|
res.setHeader('Cross-Origin-Resource-Policy', 'same-origin');
|
|
}
|
|
|
|
next();
|
|
};
|
|
|
|
module.exports = securityHeadersMiddleware;
|