From 096aa63c6fa13013f3bdf43fc9063c39c9408b89 Mon Sep 17 00:00:00 2001 From: duffyduck Date: Tue, 5 May 2026 15:22:01 +0200 Subject: [PATCH] security: Content-Security-Policy aktivieren (Pentest-Finding) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: Stage-1-Kommentar behauptete fälschlicherweise, das Frontend setze eine CSP via meta-Tag – passierte nie. Helmet-CSP war auf false, kein CSP-Header im Response. Pentest-Tool hat das richtig moniert. Fix: Helmet-CSP eingeschaltet mit SPA-tauglichen directives: default-src 'self' script-src 'self' (Vite baut Module-Scripts zu separaten Files) style-src 'self' 'unsafe-inline' (Tailwind/inline-styles) img-src self/data/blob (base64-Avatare, blob-PDFs) font-src self/data connect-src 'self' (API only) frame-ancestors 'none' (Clickjacking-Schutz, ersetzt X-Frame-Options) object-src 'none' (kein Flash/) base-uri 'self' form-action 'self' upgrade-insecure-requests Live-verifiziert: - Frontend index.html hat keine inline-scripts und keine externen Resources (Vite-Production-Build) → CSP bricht nichts. - Header gesetzt: Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; ... Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/src/index.ts | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index b37975d9..c1d3e8eb 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -81,11 +81,40 @@ app.set('trust proxy', 'loopback'); // ==================== SECURITY MIDDLEWARE ==================== -// HTTP Security Headers (X-Frame-Options, X-Content-Type-Options, HSTS, etc.) +// HTTP Security Headers (X-Frame-Options, X-Content-Type-Options, HSTS, CSP, ...) +// +// CSP ist konservativ aber SPA-tauglich: +// - script-src 'self' → keine externen Skripte, keine inline-Scripts +// (Vite baut Module-Skripte zu separaten Files, +// die sind 'self') +// - style-src 'self' 'unsafe-inline' → Tailwind/inline-Styles brauchen das +// (sicheres Trade-off; XSS via CSS ist +// marginal vs Lock-Out gegen die UI) +// - img-src self/data/blob → base64-Avatare + blob-URLs für PDFs/Downloads +// - font-src self/data → eingebettete Fonts +// - connect-src 'self' → API + WebSocket nur zur eigenen Origin +// - frame-ancestors 'none' → Clickjacking-Schutz (ersetzt X-Frame-Options) +// - object-src 'none' → keine Flash//-Embeds +// - base-uri 'self' → keine -Hijacking-Tricks +// - form-action 'self' → POST-Targets nur auf eigene Origin app.use( helmet({ - // CSP ausschalten – wird bei SPA schwierig, frontend setzt eigene CSP via meta - contentSecurityPolicy: false, + contentSecurityPolicy: { + useDefaults: true, + directives: { + 'default-src': ["'self'"], + 'script-src': ["'self'"], + 'style-src': ["'self'", "'unsafe-inline'"], + 'img-src': ["'self'", 'data:', 'blob:'], + 'font-src': ["'self'", 'data:'], + 'connect-src': ["'self'"], + 'frame-ancestors': ["'none'"], + 'object-src': ["'none'"], + 'base-uri': ["'self'"], + 'form-action': ["'self'"], + 'upgrade-insecure-requests': [], // wenn HTTPS verfügbar, dann erzwingen + }, + }, // Cross-Origin-Resource-Policy: "same-site" für SPA mit gleicher Origin crossOriginResourcePolicy: { policy: 'same-site' }, }),