Gegenbuch: externe Notarisierung der Audit-Kette
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>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# 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:
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```cron
|
||||
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=false`** existiert nur für Tests ohne hinterlegten Schlüssel.
|
||||
Im Betrieb niemals setzen.
|
||||
Reference in New Issue
Block a user