diff --git a/src/app/(app)/layout.tsx b/src/app/(app)/layout.tsx index 8cf8fc2..9861d56 100644 --- a/src/app/(app)/layout.tsx +++ b/src/app/(app)/layout.tsx @@ -8,7 +8,10 @@ import { AppShell } from "@/components/layout/app-shell"; // // import { requireSession } from "@/lib/auth/guards"; // ... -// await requireSession(); // leitet anonyme Aufrufe auf /anmelden um +// await requireSession(); // leitet anonyme Aufrufe auf /login um +// +// Kanonischer Login-Pfad: /login (siehe (auth)/login/page.tsx). Dieselbe Route +// nutzen NextAuth pages.signIn, PUBLIC_ALLOWLIST und die Gating-Specs. // // Lese-Seiten dürfen sich NICHT allein auf die Middleware verlassen. export default async function AppLayout({ diff --git a/src/app/(auth)/anmelden/page.tsx b/src/app/(auth)/login/page.tsx similarity index 100% rename from src/app/(auth)/anmelden/page.tsx rename to src/app/(auth)/login/page.tsx diff --git a/src/lib/security/headers.ts b/src/lib/security/headers.ts index 76ba98c..d593462 100644 --- a/src/lib/security/headers.ts +++ b/src/lib/security/headers.ts @@ -1,8 +1,33 @@ /** * Sicherheits-Header, eingehängt in next.config.ts. - * CSP bewusst konservativ; bei Bedarf von Feature-Workstreams erweitert. + * + * Content-Security-Policy ist der zentrale Querschnitts-Schutz (Implementierungs- + * plan Z.1314): in Produktion strikt mit default-src 'self', frame-ancestors 'none' + * und form-action 'self'. Im Dev-Modus benötigt Next.js (HMR/React-Refresh) eine + * gelockerte script-src/connect-src-Variante ('unsafe-eval' + ws: für den Dev-Socket). */ +const isProd = process.env.NODE_ENV === "production"; + +const CSP = [ + "default-src 'self'", + // Dev braucht eval (React Refresh) + inline; Prod bleibt strikt. + isProd + ? "script-src 'self'" + : "script-src 'self' 'unsafe-eval' 'unsafe-inline'", + "style-src 'self' 'unsafe-inline'", + "img-src 'self' data: blob:", + "font-src 'self' data:", + "worker-src 'self' blob:", + // Dev: WebSocket für HMR erlauben. + isProd ? "connect-src 'self'" : "connect-src 'self' ws: wss:", + "frame-ancestors 'none'", + "form-action 'self'", + "base-uri 'self'", + "object-src 'none'", +].join("; "); + export const SECURITY_HEADERS: Record = { + "Content-Security-Policy": CSP, "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", "Referrer-Policy": "strict-origin-when-cross-origin",