R175-01 (HIGH): Die erste Fassung signierte zwar, prueft aber nie. Sie las ihre
Wahrheit per readFileSync aus der lokalen Arbeitsdatei, nirgends gab es ein
git verify-commit - das -S war write-only ohne Konsument. Live reproduziert:
DB-Tail abgeschnitten und die lokale Ledger-Zeile angepasst -> "OK, Checkpoint
beglaubigt", exit 0, kein Alarm.
Fix: Wahrheitsquelle ist der signierte Commit-Baum (bevorzugt der Remote-Kopf);
jeder Commit mit Gegenbuch-Aenderung muss eine gueltige Signatur tragen; weicht
die Arbeitsdatei vom signierten Stand ab, wird abgebrochen; lokale, nie
gepushte Commits gelten nicht als beglaubigt; die Signatur des frisch
erzeugten Commits wird gegengeprueft. Dazu ein Pruefmodus --check fuer
Auditoren.
R175-02 (MEDIUM): /checkpoint fuhr je Aufruf ein volles verifyIntegrity (O(n),
0,85 s bei 16k Zeilen) - authentifizierte DoS-Verstaerkung. Gebraucht wurde nur
die Siegel-Wurzel. Jetzt Kopf-Hash aus der Kopfzeile, Wurzel aus dem juengsten
gueltigen Marker, sealLeafCount statt des teuren Status.
R175-03: writeFileSync lief vor dem Commit, eine verwaiste Zeile wurde vom
Folgelauf zementiert. Jetzt Ruecknahme bei Fehlschlag, und NOTARY_SIGN=false
verlangt zusaetzlich NOTARY_INSECURE_ACK.
Verifiziert mit echtem SSH-Signaturschluessel: stilles Waschen -> Alarm;
erfundene Zeile -> Alarm; unsignierter Commit -> Alarm; Commit-Fehlschlag ->
zurueckgerollt; Reflex-Schalter verweigert; Pruefmodus haengt nichts an.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Abschluss der Anker-Kette. Alle bisherigen Schutzebenen liegen in derselben
Datenbank, die sie absichern sollen - der Pentest hat das ueber mehrere Runden
Schicht fuer Schicht gezeigt, zuletzt in R174-01 am Siegel-Marker selbst.
Aufteilung nach der Analyse des Pentesters (der Schutz kommt vom Ort, nicht von
der Signatur): Das CRM liefert nur einen lesbaren Kontrollwert ohne Geheimnisse
(GET /api/audit-logs/checkpoint, audit:read). Signiert, zeitgestempelt und
angehaengt wird auf einem anderen Rechner - Schluessel und Push-Recht liegen
nicht in den Deploy-Secrets des CRM. Ohne diese Trennung waere es D1 nochmal,
nur schlimmer: sieht nach doppeltem Boden aus, tut still nichts.
Der Kontrollwert enthaelt bewusst maxId. Ein blosser Kopf-Hash erkennt
Umschreiben, aber kein Abschneiden am Ende - genau die R174-01-Klasse, eine
Ebene hoeher. atId erlaubt der Gegenstelle, einen frueher beglaubigten Kopf
erneut abzufragen und nachzurechnen.
Gegenstelle: tools/audit-notary/notary.mjs (Cron auf zweitem Rechner, privates
Git-Repo als Append-only-Ablage, signierte Commits). Prueft vor dem Anhaengen
und bricht bei Widerspruch mit Exit-Code 2 ab, ohne zu schreiben.
Verifiziert gegen eine CRM-Attrappe mit echter DB: beglaubigte Zeile veraendert
-> Alarm; am Ende abgeschnitten (maxId 5->4) -> Alarm; Gegenbuch selbst
gekuerzt (seq-Luecke) -> Alarm; in allen Faellen nichts angehaengt.
Ehrlich dokumentiert: Restfenster zwischen zwei Laeufen bleibt und ist
inhaerent; ein stiller Cron-Ausfall erzeugt im CRM keine Warnung und muss auf
dem Gegenbuch-Rechner ueberwacht werden; Force-Push muss serverseitig gesperrt
sein, sonst ist Append-only nur geliehen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>