diff --git a/DOCKER.md b/DOCKER.md index 56eed02..fc8bf13 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -68,6 +68,8 @@ docker-compose up -d | `PORT` | No | `3000` | Port the server listens on | | `ALLOWED_ORIGINS` | No | `*` | Comma-separated list of allowed CORS origins | | `NODE_ENV` | No | - | Set to `production` for production deployments | +| `UPGRADE_INSECURE_REQUESTS` | No | `false` | Set to `true` to enable CSP upgrade-insecure-requests directive. **Only use when deployed behind an HTTPS reverse proxy (nginx, Traefik, etc.) with valid SSL certificates.** This upgrades all HTTP requests to HTTPS and will break direct HTTP access. | +| `X_FRAME_OPTIONS` | No | `false` | Set to `true` to enable X-Frame-Options: SAMEORIGIN header to prevent clickjacking. **Disables iframe embedding when enabled.** | ### Authentication Modes diff --git a/src/server.ts b/src/server.ts index e245dcb..3b0d948 100644 --- a/src/server.ts +++ b/src/server.ts @@ -20,6 +20,10 @@ const CREDENTIALS_PATH = path.join(process.cwd(), 'data', 'credentials.json') const MAX_FILE_SIZE = 16 * 1024 * 1024 // 16MB limit for file uploads const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : ['*'] const isProduction = process.env.NODE_ENV === 'production' +// Enable upgrade-insecure-requests only when behind HTTPS reverse proxy +const enableUpgradeInsecure = process.env.UPGRADE_INSECURE_REQUESTS === 'true' +// Enable X-Frame-Options header to prevent iframe embedding (disabled by default) +const enableXFrameOptions = process.env.X_FRAME_OPTIONS === 'true' /** * Validates and sanitizes file paths to prevent path traversal attacks @@ -83,6 +87,7 @@ async function startServer() { styleSrc: ["'self'", "'unsafe-inline'"], // Required for Material-UI connectSrc: ["'self'", 'ws:', 'wss:'], // Allow WebSocket connections imgSrc: ["'self'", 'data:', 'blob:'], + upgradeInsecureRequests: enableUpgradeInsecure ? [] : null, // Only enable when behind HTTPS reverse proxy }, }, hsts: isProduction @@ -92,6 +97,7 @@ async function startServer() { preload: true, } : false, + frameguard: enableXFrameOptions ? { action: 'sameorigin' } : false, // Disabled by default to allow iframe embedding // Disable cross-origin policies that cause blank pages when accessing via IP vs localhost // These headers can block resources and cause rendering issues on HTTP-only deployments crossOriginEmbedderPolicy: false, // Can block resources without proper CORP headers