Audit-Siegel: Umstiegsweg dokumentiert + Rotations-Fussangel entschaerft
Frage aus dem Betrieb: bestehende Installation hat noch keinen AUDIT_HMAC_KEY - was passiert beim nachtraeglichen Setzen? Antwort jetzt in README und .env.example: setzen, neu starten, fertig. Bestehende Eintraege bleiben unveraendert gueltig, neue werden gesiegelt, alt und neu koexistieren ohne Fehlalarm. Nachgemessen auf gemischtem Bestand (4903 x V1, 57 x V2, 67 x V3): 0 Beanstandungen. Rueckwirkend siegeln ist nicht moeglich. Dabei zwei Fehler in der eigenen Doku gefunden und korrigiert: 1. Behauptet war, nach dem Leeren von AUDIT_HMAC_KEY_OLD seien alte Eintraege "nicht mehr pruefbar". Tatsaechlich werden sie als MANIPULIERT gemeldet - ein falscher Schluessel ist von einer Faelschung nicht zu unterscheiden. Gemessen: 67 Eintraege als manipuliert. Nur wenn GAR KEIN Schluessel gesetzt ist, gilt "nicht pruefbar". Doku entsprechend korrigiert, inkl. Warnung, das Feld nicht voreilig zu leeren. 2. AUDIT_HMAC_KEY_OLD bot nur einen Platz. Beim ZWEITEN Wechsel waeren alle mit dem ersten Schluessel gesiegelten Eintraege faelschlich als manipuliert erschienen (reproduziert: 67). Das Feld nimmt jetzt eine kommagetrennte Liste entgegen; verifiziert: mit beiden Alt-Schluesseln 0 Beanstandungen, mit nur dem juengsten 67. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -227,9 +227,16 @@ function auditHmacKey(): string | null {
|
||||
return k && k.trim().length > 0 ? k : null;
|
||||
}
|
||||
|
||||
function auditHmacKeyOld(): string | null {
|
||||
const k = process.env.AUDIT_HMAC_KEY_OLD;
|
||||
return k && k.trim().length > 0 ? k : null;
|
||||
/**
|
||||
* Frueher verwendete Schluessel. Kommagetrennt, damit auch ein ZWEITER Wechsel
|
||||
* moeglich ist, ohne die zuerst signierten Eintraege zu verlieren - mit nur
|
||||
* einem Platz wuerden die aeltesten sonst als "manipuliert" gemeldet.
|
||||
*/
|
||||
function auditHmacKeysOld(): string[] {
|
||||
return (process.env.AUDIT_HMAC_KEY_OLD || '')
|
||||
.split(',')
|
||||
.map((k) => k.trim())
|
||||
.filter((k) => k.length > 0);
|
||||
}
|
||||
|
||||
function generateHashV3(data: AuditHashV2Input, key: string): string {
|
||||
@@ -765,17 +772,18 @@ export async function verifyIntegrity(fromId?: number, toId?: number): Promise<{
|
||||
};
|
||||
|
||||
if (erwartet === 3) {
|
||||
// HMAC-signiert: ohne Schluessel ist die Zeile nicht pruefbar. Das als
|
||||
// "manipuliert" zu melden waere ein Fehlalarm – daher eigener Topf.
|
||||
const key = auditHmacKey();
|
||||
const keyOld = auditHmacKeyOld();
|
||||
if (!key && !keyOld) {
|
||||
// HMAC-signiert: ohne JEDEN Schluessel ist die Zeile nicht pruefbar.
|
||||
// Das als "manipuliert" zu melden waere ein Fehlalarm – daher eigener
|
||||
// Topf. Ein FALSCHER Schluessel ist dagegen nicht von einer Faelschung
|
||||
// zu unterscheiden und wird bewusst als Befund gemeldet.
|
||||
const kandidaten = [auditHmacKey(), ...auditHmacKeysOld()].filter(
|
||||
(k): k is string => !!k,
|
||||
);
|
||||
if (kandidaten.length === 0) {
|
||||
unverifiableEntries.push(log.id);
|
||||
continue;
|
||||
}
|
||||
// keyOld deckt den Zeitraum vor einem Schluesselwechsel ab.
|
||||
hashOk = (!!key && log.hash === generateHashV3(inhalt, key))
|
||||
|| (!!keyOld && log.hash === generateHashV3(inhalt, keyOld));
|
||||
hashOk = kandidaten.some((k) => log.hash === generateHashV3(inhalt, k));
|
||||
} else {
|
||||
hashOk = log.hash === generateHashV2(inhalt);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user