Audit-Siegel: Platzhalter zaehlt nicht als Schluessel + Betriebsfalle dokumentiert

.env.example zeigt jetzt wie bei den anderen Secrets einen sichtbaren
Platzhalter an der Variablen statt eines leeren Werts - verstaendlicher, aber
mit Schutz dagegen: ein nicht ersetzter Platzhalter (enthaelt < oder >, oder
beginnt mit change/dein/your/hier) gilt NICHT als Schluessel. Sonst wuerde mit
einem oeffentlich im Repository stehenden Wert gesiegelt, was Sicherheit
vortaeuscht. Das Backend warnt in dem Fall im Log und laesst das Siegel aus.
Verifiziert: mit Platzhalter geschriebene Zeile bleibt hashVersion 2.

Dabei aufgefallen und dokumentiert: Wird das Siegel nach Aktivierung wieder
abgeschaltet (Schluessel fehlt, z. B. beim Rebuild verloren), sind die in
dieser Zeit entstandenen Eintraege ungesiegelt und werden beanstandet, sobald
der Schluessel zurueck ist. Das ist Absicht - ein ungesiegelter Eintrag
inmitten gesiegelter ist von einer Faelschung nicht zu unterscheiden. Warnung
in README und .env.example.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 19:24:59 +02:00
co-authored by Claude Opus 5
parent 6de3a91aa7
commit ab0d6214f2
3 changed files with 52 additions and 10 deletions
+26 -3
View File
@@ -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.
* `<hier-eigenen-wert-eintragen>`) 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 {