Gegenbuch: verifizierender Leser statt Absichtserklaerung (Pentest R175)
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>
This commit is contained in:
@@ -43,6 +43,32 @@ Als Cronjob, stündlich:
|
||||
nur geliehen – wer Schreibrecht auf das Repository erlangt, schreibt die
|
||||
Historie sonst einfach um. Bei GitHub/GitLab: Branch-Protection auf `main`.
|
||||
|
||||
## Der entscheidende Punkt: es wird tatsächlich geprüft
|
||||
|
||||
Die erste Fassung dieses Skripts hat signiert – aber **nie eine Signatur
|
||||
geprüft**. Es las seine Wahrheit aus der lokalen Arbeitsdatei. Wer den
|
||||
Notar-Rechner beschreiben konnte, hat ihm damit beliebige „beglaubigte"
|
||||
Zeilen untergeschoben und einen abgeschnittenen Datenbestand als neue Wahrheit
|
||||
festgeschrieben, ohne Alarm. Signieren ohne prüfenden Leser ist wertlos.
|
||||
|
||||
Deshalb gilt jetzt:
|
||||
|
||||
- Wahrheitsquelle ist der **signierte Commit-Baum** (bevorzugt der Remote-Kopf),
|
||||
nicht die Arbeitsdatei
|
||||
- **Jeder** Commit, der das Gegenbuch ändert, muss eine gültige Signatur tragen
|
||||
- Weicht die Arbeitsdatei vom signierten Stand ab, wird abgebrochen
|
||||
- Geschrieben wird erst nach erfolgreichem, signiertem Commit – scheitert er,
|
||||
wird die Datei zurückgerollt
|
||||
|
||||
## Prüfmodus für Auditoren
|
||||
|
||||
```bash
|
||||
node notary.mjs --check
|
||||
```
|
||||
|
||||
Führt alle Kontrollen aus, hängt aber nichts an und braucht kein Schreibrecht.
|
||||
Geeignet für jemanden, der die Kette unabhängig nachvollziehen will.
|
||||
|
||||
## Was das Skript erkennt
|
||||
|
||||
| Angriff | Erkennung |
|
||||
@@ -51,6 +77,10 @@ Historie sonst einfach um. Bei GitHub/GitLab: Branch-Protection auf `main`.
|
||||
| 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 |
|
||||
| Gegenbuch lokal manipuliert | Arbeitsdatei weicht vom signierten Stand ab |
|
||||
| Untergeschobener Commit | Commit ohne gültige Signatur in der Historie |
|
||||
| Nie gepushte lokale Commits | Abgleich gegen den Remote-Kopf |
|
||||
| Bestandssiegel-Blätter entfernt | beglaubigte Blattzahl auf null gefallen |
|
||||
|
||||
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
|
||||
@@ -67,5 +97,11 @@ im Log unter.
|
||||
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.
|
||||
- **`NOTARY_SIGN=false`** existiert nur für Tests ohne hinterlegten Schlüssel
|
||||
und verlangt zusätzlich `NOTARY_INSECURE_ACK=…`. Der Reflex „Cron hakt, also
|
||||
Signatur abschalten" soll bewusst nicht ausreichen – er würde eine
|
||||
ungeschützte Kette festschreiben.
|
||||
- **Der Remote-Server muss Force-Push verhindern.** Das Skript prüft die
|
||||
Signaturen, aber wenn jemand die Historie am Server überschreiben kann,
|
||||
ersetzt er auch die signierten Commits durch eigene. Branch-Protection ist
|
||||
Teil des Schutzes, nicht Zubehör.
|
||||
|
||||
Reference in New Issue
Block a user