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>
2.9 KiB
Gegenbuch (externe Notarisierung)
Alle Schutzmechanismen im CRM selbst – Hash-Kette, Versionsgrenze, Löschungs-Manifest, Bestandssiegel – liegen in derselben Datenbank, die sie absichern sollen. Wer dort schreiben kann, sitzt am Ende immer schon auf der Ebene, die den Beweis führt. Genau das hat der Pentest über mehrere Runden Schicht für Schicht gezeigt.
Das Gegenbuch durchbricht das: Ein zweiter Rechner holt regelmäßig einen kurzen Kontrollwert vom CRM, prüft ihn gegen seine eigene Historie und hängt ihn signiert an ein privates Repository an.
Die eine nicht verhandelbare Bedingung
Der Signaturschlüssel und das Push-Recht dürfen nicht in den Deploy-Secrets des CRM liegen. Sonst ist der zweite Schlüssel im selben Zugriffsbereich wie der erste – es sieht nach doppeltem Boden aus und ist keiner. Das CRM braucht für dieses Verfahren gar nichts zu wissen: Es liefert nur einen lesbaren Kontrollwert, der keine Geheimnisse enthält.
Einrichten
Auf einem anderen Rechner als dem CRM-Server:
git clone git@host:dein/audit-gegenbuch.git
cd audit-gegenbuch
export CRM_URL=https://crm.example.de
export CRM_TOKEN=<Access-Token eines Benutzers mit audit:read>
node /pfad/zu/notary.mjs
Als Cronjob, stündlich:
0 * * * * cd /pfad/zum/gegenbuch && CRM_URL=https://crm.example.de \
CRM_TOKEN=... node /pfad/notary.mjs >> notary.log 2>&1
Force-Push serverseitig sperren. Ohne das ist die Append-only-Eigenschaft
nur geliehen – wer Schreibrecht auf das Repository erlangt, schreibt die
Historie sonst einfach um. Bei GitHub/GitLab: Branch-Protection auf main.
Was das Skript erkennt
| Angriff | Erkennung |
|---|---|
| Beglaubigter Eintrag nachträglich verändert | Hash an der beglaubigten Position stimmt nicht mehr |
| Einträge am Ende abgeschnitten | aktuelle höchste ID kleiner als die beglaubigte |
| Bestandssiegel verschwunden | vorher beglaubigt, jetzt nicht mehr vorhanden |
| Gegenbuch selbst gekürzt | Lücke in der fortlaufenden Nummer |
Bei jedem dieser Fälle bricht das Skript mit Exit-Code 2 ab und hängt nichts an – der manipulierte Zustand wird also nicht als neue Wahrheit festgeschrieben. Für Cron heißt das: Exit-Code überwachen, sonst geht der Alarm im Log unter.
Was es nicht leistet – ehrlich
- Restfenster: Einträge zwischen dem letzten Kontrollwert und jetzt sind noch nicht beglaubigt und am Ende fälsch- oder kürzbar. Häufigerer Lauf verkleinert das Fenster, beseitigt es aber nicht. Das gilt für jedes periodische Verfahren dieser Art.
- Stiller Ausfall: Läuft der Cronjob nicht mehr, entsteht keine Warnung im CRM – dort ist ja nichts konfiguriert. Der Ausfall muss auf dem Gegenbuch-Rechner überwacht werden (z. B. Alarm, wenn der jüngste Commit älter als zwei Intervalle ist).
NOTARY_SIGN=falseexistiert nur für Tests ohne hinterlegten Schlüssel. Im Betrieb niemals setzen.