diff --git a/.env.example b/.env.example index 0abe1034..36909c0f 100644 --- a/.env.example +++ b/.env.example @@ -52,6 +52,12 @@ LISTEN_ADDR=0.0.0.0 # In Docker = 0.0.0.0, in Bare-Metal-Production = 127 # Beispiel: CORS_ORIGINS=https://crm.deine-domain.de # CORS_ORIGINS= +# HTTPS-only-Header (HSTS + upgrade-insecure-requests) – NUR aktivieren, wenn +# wirklich ein TLS-Proxy (Caddy/Traefik/Nginx) vor OpenCRM steht. Sonst sperrt +# sich der Browser bei direktem http://ip:port-Zugriff selbst aus +# (ERR_SSL_PROTOCOL_ERROR auf den Assets). +HTTPS_ENABLED=false + # ============== ADMINER (DB-UI) ============== # Theme-Auswahl. Verfügbare Designs im offiziellen adminer:latest Image: # adminer-dark, brade, bueltge, dracula, esterka, flat, galkaev, diff --git a/backend/src/index.ts b/backend/src/index.ts index def4be12..37e8f001 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -151,6 +151,13 @@ app.use((_req, res, next) => { next(); }); +// HTTPS-only-Header (HSTS + upgrade-insecure-requests) nur setzen, wenn +// wirklich TLS davor läuft – sonst sperrt sich die App auf direkt-via-IP- +// Deployments (Browser versucht /assets/* via https zu laden → SSL-Error). +// Aktivieren mit HTTPS_ENABLED=true in der .env, sobald ein TLS-Proxy +// (Caddy/Traefik/Nginx) vor OpenCRM steht. +const httpsEnabled = process.env.HTTPS_ENABLED === 'true'; + app.use( helmet({ contentSecurityPolicy: { @@ -166,10 +173,16 @@ app.use( 'object-src': ["'none'"], 'base-uri': ["'self'"], 'form-action': ["'self'"], - 'upgrade-insecure-requests': [], // wenn HTTPS verfügbar, dann erzwingen + // useDefaults bringt 'upgrade-insecure-requests' selbst mit – explizit + // auf null setzen entfernt es aus dem Header (helmet-API). + 'upgrade-insecure-requests': httpsEnabled ? [] : null, }, }, - // Cross-Origin-Resource-Policy: "same-site" für SPA mit gleicher Origin + // HSTS nur wenn echt TLS vorhanden – sonst sperrt sich der Browser + // dauerhaft aus, wenn die App direkt via http://ip:port erreichbar ist. + strictTransportSecurity: httpsEnabled + ? { maxAge: 31536000, includeSubDomains: true } + : false, crossOriginResourcePolicy: { policy: 'same-site' }, }), ); diff --git a/docker-compose.yml b/docker-compose.yml index 85e00992..5329e4d0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -65,6 +65,7 @@ services: PORT: 3001 LISTEN_ADDR: 0.0.0.0 CORS_ORIGINS: ${CORS_ORIGINS:-} + HTTPS_ENABLED: ${HTTPS_ENABLED:-false} RUN_SEED: ${RUN_SEED:-false} ports: - "${OPENCRM_PORT:-3010}:3001"