From f439d0b318f57032221e5fd1c0f9bbb06304aced Mon Sep 17 00:00:00 2001 From: paul Date: Sun, 13 Jul 2025 19:48:28 +0200 Subject: [PATCH] fix(security): enhance security headers and tighten CORS configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Configure custom Content Security Policy for React app compatibility - Add Permissions-Policy header to disable unnecessary browser features - Set HSTS to 1 year with preload flag for better transport security - Update referrer policy to strict-origin-when-cross-origin - Restrict CORS localhost origins to development environment only - Production deployments now only allow configured FRONTEND_URL and ADMIN_URL - Addresses security headers configuration issues from security scan 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- backend/server.js | 48 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/backend/server.js b/backend/server.js index 32522fa..f2618b8 100644 --- a/backend/server.js +++ b/backend/server.js @@ -28,21 +28,55 @@ const adminAuthRoutes = require('./src/routes/adminAuth'); const app = express(); const PORT = process.env.PORT || 3000; -// Security middleware -app.use(helmet()); +// Security middleware with custom CSP +app.use(helmet({ + contentSecurityPolicy: { + directives: { + defaultSrc: ["'self'"], + scriptSrc: ["'self'", "'unsafe-inline'"], // Required for React + styleSrc: ["'self'", "'unsafe-inline'", "https:"], // Required for styled components + imgSrc: ["'self'", "data:", "https:", "blob:"], // Allow data URLs and external images + connectSrc: ["'self'"], // API connections + fontSrc: ["'self'", "https:", "data:"], // Web fonts + objectSrc: ["'none'"], // Disable plugins + mediaSrc: ["'self'"], // Audio/video + frameSrc: ["'none'"], // Disable iframes + }, + }, + hsts: { + maxAge: 31536000, // 1 year + includeSubDomains: true, + preload: true + }, + permittedCrossDomainPolicies: false, + referrerPolicy: { policy: "strict-origin-when-cross-origin" } +})); + +// Additional security headers +app.use((req, res, next) => { + // Permissions Policy (controls browser features) + res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=()'); + next(); +}); // CORS configuration const corsOptions = { origin: function (origin, callback) { const allowedOrigins = [ process.env.FRONTEND_URL || 'http://localhost:3005', - process.env.ADMIN_URL || 'http://localhost:3005', - 'http://localhost:5173', // Vite dev server - 'http://localhost:3002', // Backend server - 'http://localhost:3001', // For API testing - 'http://localhost:3000' // Direct backend access + process.env.ADMIN_URL || 'http://localhost:3005' ]; + // In development, also allow localhost origins + if (process.env.NODE_ENV === 'development') { + allowedOrigins.push( + 'http://localhost:5173', // Vite dev server + 'http://localhost:3002', // Backend server + 'http://localhost:3001', // For API testing + 'http://localhost:3000' // Direct backend access + ); + } + // Allow requests with no origin (like mobile apps or curl) if (!origin || allowedOrigins.indexOf(origin) !== -1) { callback(null, true);