From 7bd2a3dc3a31dd3a4cc3820c72601efad368e088 Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Wed, 2 Sep 2026 08:45:56 +0200 Subject: [PATCH] feat: Sicherheits-Header fuer das Frontend ergaenzt apicore.encry.de lieferte bisher gar keine Security-Header aus (per curl bestaetigt) - CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, HSTS fehlten komplett, waehrend das Backend das laengst per helmet() macht. Ergaenzt fuer alle Routen. Bewusst ohne CSP-Nonce (haette dynamic rendering fuer jede Seite erzwungen und damit die bestehende statische Optimierung gekostet) - stattdessen script-src/style-src 'unsafe-inline', aber default-src/connect-src/ img-src/object-src/frame-ancestors/form-action/base-uri strikt eingeschraenkt. Lokal mit dem echten Production-Build (standalone server + next.config.ts) verifiziert: Login-Seite rendert/hydratisiert sauber, keine CSP-Verstoesse in der Konsole, Header per curl bestaetigt. --- ApiCore-Frontend/next.config.ts | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/ApiCore-Frontend/next.config.ts b/ApiCore-Frontend/next.config.ts index afea59d..81d405e 100644 --- a/ApiCore-Frontend/next.config.ts +++ b/ApiCore-Frontend/next.config.ts @@ -1,5 +1,44 @@ import type { NextConfig } from "next"; +// Runs when the Node server process starts inside the container, so this +// correctly reads the deployment's actual runtime API_URL (see +// src/lib/config/runtime-config.ts for why API_URL is a runtime env var +// rather than baked into the client bundle at build time) - this is server +// startup, not the browser bundle, so it isn't subject to that constraint. +function apiOrigin(): string { + try { + return new URL(process.env.API_URL ?? "http://localhost:3001").origin; + } catch { + return ""; + } +} + +// No CSP nonce here on purpose: nonce-based CSP requires every page to be +// dynamically rendered (Next.js CSP guide, "Static vs Dynamic Rendering"), +// which would drop this app's static optimization across the board. This +// policy still blocks the things that matter for a mostly-same-origin app - +// cross-origin exfiltration, clickjacking, base-tag/form hijacking - via +// 'unsafe-inline' only for script/style (needed for Next's own hydration +// data and Tailwind's inline styles), matching the same trade-off the +// backend's own helmet CSP already makes for style-src. +function cspHeader(): string { + return ` + default-src 'self'; + script-src 'self' 'unsafe-inline'; + style-src 'self' 'unsafe-inline'; + img-src 'self' blob: data:; + font-src 'self'; + connect-src 'self' ${apiOrigin()}; + object-src 'none'; + base-uri 'self'; + form-action 'self'; + frame-ancestors 'self'; + upgrade-insecure-requests; + ` + .replace(/\s{2,}/g, " ") + .trim(); +} + const nextConfig: NextConfig = { output: "standalone", async headers() { @@ -13,6 +52,16 @@ const nextConfig: NextConfig = { source: "/sw.js", headers: [{ key: "Cache-Control", value: "no-cache, no-store, must-revalidate" }], }, + { + source: "/:path*", + headers: [ + { key: "Content-Security-Policy", value: cspHeader() }, + { key: "X-Frame-Options", value: "SAMEORIGIN" }, + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "Referrer-Policy", value: "no-referrer" }, + { key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains" }, + ], + }, ]; }, };