diff --git a/README.md b/README.md index c28e6084..9eaaba2a 100644 --- a/README.md +++ b/README.md @@ -365,6 +365,19 @@ ungesiegelt. Das ist kein Fehler, sondern liegt in der Natur der Sache: Ein Siegel kann man nur beim Anlegen vergeben, nicht nachträglich. Je früher du den Schlüssel setzt, desto größer der geschützte Zeitraum. +> ⚠️ **Einmal gesetzt, nicht wieder abschalten.** Fehlt der Schlüssel später +> (z. B. weil er beim Container-Rebuild aus der `.env` verlorenging), läuft die +> Anwendung normal weiter – aber die in dieser Zeit entstandenen Einträge sind +> ungesiegelt und werden von der Prüfung **beanstandet**, sobald der Schlüssel +> wieder da ist. Das ist Absicht: Ein ungesiegelter Eintrag inmitten +> gesiegelter ist von einer Fälschung nicht zu unterscheiden. Nimm den +> Schlüssel deshalb in deine Deploy-Checkliste auf. + +Ein noch nicht ersetzter Platzhalter (``) zählt +bewusst **nicht** als Schlüssel – sonst würde mit einem öffentlich bekannten +Wert gesiegelt. Das Backend schreibt in diesem Fall eine Warnung ins Log und +lässt das Siegel aus. + **Häufige Fragen** | Frage | Antwort | diff --git a/backend/.env.example b/backend/.env.example index c3cf797b..fe3cde33 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -53,6 +53,13 @@ NODE_ENV=development # dadurch KEINE Fehlalarme. Rueckwirkend siegeln geht nicht - je frueher # gesetzt, desto groesser der geschuetzte Zeitraum. # +# ACHTUNG: einmal gesetzt, nicht wieder abschalten. +# Fehlt der Schluessel spaeter (z. B. beim Container-Rebuild verlorengegangen), +# laeuft alles weiter - aber die in dieser Zeit entstandenen Eintraege sind +# ungesiegelt und werden beanstandet, sobald der Schluessel wieder da ist. +# Ein ungesiegelter Eintrag inmitten gesiegelter ist von einer Faelschung +# nicht zu unterscheiden. Also: in die Deploy-Checkliste aufnehmen. +# # Was passiert, wenn ich ihn verliere? # Laesst du das Feld LEER, gelten die gesiegelten Eintraege als # "nicht pruefbar" - NICHT als gefaelscht, also kein Fehlalarm. Der @@ -62,11 +69,11 @@ NODE_ENV=development # ein falscher Schluessel ist von einer Faelschung nicht zu unterscheiden. # Deshalb: Schluessel sichern, so wie ein Passwort. # -# Hier steht bewusst KEIN Beispielwert: leer = Siegel aus (nichts faellt aus). -# Ein Beispielwert waere gefaehrlicher als keiner - er stuende oeffentlich im -# Repository, und jeder koennte damit Eintraege siegeln. Trage deinen eigenen, -# selbst erzeugten Wert ein: -AUDIT_HMAC_KEY= +# Eigenen Wert erzeugen mit: openssl rand -hex 32 +# Der Platzhalter unten zaehlt NICHT als Schluessel - laesst du ihn stehen, +# bleibt das Siegel aus (es wird also nicht versehentlich mit einem oeffentlich +# bekannten Wert gesiegelt). Leer lassen ist ebenfalls in Ordnung. +AUDIT_HMAC_KEY= # Frueher verwendete Schluessel - beim Wechsel hier eintragen. # Moechtest du den Schluessel oben austauschen (z. B. weil du vermutest, @@ -83,8 +90,7 @@ AUDIT_HMAC_KEY= # Mehrfach gewechselt? Mehrere Schluessel kommagetrennt, juengster zuerst: # AUDIT_HMAC_KEY_OLD=,, # -# Beim ersten Einrichten bleibt das Feld leer - es wird erst beim Wechsel -# gebraucht. +# Beim ersten Einrichten leer lassen - erst beim Schluesselwechsel noetig. AUDIT_HMAC_KEY_OLD= # --- Technisch (fuer Entwickler/Admins) --------------------------------- diff --git a/backend/src/services/audit.service.ts b/backend/src/services/audit.service.ts index 4dffea1c..0b650eee 100644 --- a/backend/src/services/audit.service.ts +++ b/backend/src/services/audit.service.ts @@ -222,9 +222,32 @@ export interface AuditHashV2Input { * `AUDIT_HMAC_KEY_OLD` erlaubt einen Schluesselwechsel ohne Rehash – bei der * Pruefung wird zusaetzlich gegen den alten Schluessel getestet. */ +/** + * Ein nicht ersetzter Platzhalter aus `.env.example` (z. B. + * ``) darf NICHT als Schluessel gelten. Sonst + * wuerde mit einem oeffentlich im Repository stehenden Wert gesiegelt – das + * waere schlechter als gar kein Siegel, weil es Sicherheit vortaeuscht. + */ +function istPlatzhalter(k: string): boolean { + return k.includes('<') || k.includes('>') || /^(change|dein|your|hier)/i.test(k); +} + +let platzhalterGemeldet = false; + function auditHmacKey(): string | null { - const k = process.env.AUDIT_HMAC_KEY; - return k && k.trim().length > 0 ? k : null; + const k = process.env.AUDIT_HMAC_KEY?.trim(); + if (!k) return null; + if (istPlatzhalter(k)) { + if (!platzhalterGemeldet) { + platzhalterGemeldet = true; + console.warn( + '[Audit] AUDIT_HMAC_KEY enthält noch den Platzhalter aus .env.example – ' + + 'das Audit-Siegel bleibt deaktiviert. Eigenen Wert erzeugen: openssl rand -hex 32', + ); + } + return null; + } + return k; } /** @@ -236,7 +259,7 @@ function auditHmacKeysOld(): string[] { return (process.env.AUDIT_HMAC_KEY_OLD || '') .split(',') .map((k) => k.trim()) - .filter((k) => k.length > 0); + .filter((k) => k.length > 0 && !istPlatzhalter(k)); } function generateHashV3(data: AuditHashV2Input, key: string): string {