Siegelwechsel ist ein Alarm, kein Hinweis (Pentest R185-01/-02)
R185-01 (MEDIUM): Die Flanke, die wir selbst gemeldet hatten, hat der
Tester live bestaetigt. seal-backlog war beim ZWEITEN Aufruf genauso
gegatet wie beim ersten ({"confirm":"SEAL"} -> 200), und das Ereignis
landete nur im Audit-Log, nicht im Alarmkanal. Sein Punkt: der
automatische Rueckhalt des Gegenbuchs haengt an `valid` - und `valid`
ueberlebt ein ersetzendes Siegel per Konstruktion. Angriff: Altzeile per
DB-Zugriff loeschen, neu siegeln, Luecke ist beglaubigt, valid wieder
true. Live reproduziert.
Zwei Schichten, in seiner Reihenfolge:
1. Alarmkanal. Neuer SecurityEventType AUDIT_SEAL_CHANGED (Migration
20260826120000). Erstes Siegeln HIGH, Ersetzen CRITICAL - geht damit
ueber sendPendingCriticalAlerts sofort per Mail raus. Die Details
halten Wurzel vorher/nachher und den vollstaendigen Vorbefund fest.
2. Gate. Steht bereits ein Siegel, verlangt der Endpunkt
{"confirm":"RESEAL"} statt SEAL, mit einem Text, der sagt, was dabei
verloren geht. Ein Austausch der Beweisgrundlage soll nicht dasselbe
Wort haben wie das Einrichten.
Und im Gegenbuch selbst: dort stand fuer den Wurzelwechsel ein
console.warn, waehrend der Rueckgabecode auf 0 blieb - also exakt das
Muster, das wir dem CRM zweimal angekreidet haben (R179, R183-02), im
Werkzeug, das dagegen gebaut wurde. Jetzt exit 2, mit alter und neuer
Wurzel samt Blattzahl; "10 Blaetter -> 9 Blaetter" zeigt die Loeschung
sofort. Auch die Erstsiegelung meldet sich, statt stillschweigend
uebernommen zu werden.
Aufloesbar gemacht: der Alarm bricht ab, BEVOR angehaengt wird - ohne
Bestaetigungsweg haette auch ein legitimes Siegeln fuer immer alarmiert
(R183-03-Falle). Neu ist NOTARY_SEAL_ACK, bewusst nicht "true", sondern
die Wurzel selbst (mind. 16 Zeichen): ein stehen gelassener Wert passt
beim naechsten Wechsel nicht mehr und kann keinen weiteren Austausch
durchwinken.
R185-02 (LOW): GET /api/audit-logs?action=<x> gab ungueltige Enum-Werte
roh an die Spalte -> 500. Zweifach schlecht: fehlende Validierung und
Fehler-Orakel (200 vs 500 verraet die Enum-Mitglieder). Jetzt 400 mit
der erlaubten Menge im Klartext. Mitgenommen: sensitivity, Datumsfelder,
Zahlenfelder, Textlaengen und ein Deckel auf limit (200), ueber den sich
sonst die ganze Tabelle an der Seitenlogik vorbei ziehen liess. Beide
Endpunkte.
Getestet ueber HTTP gegen eine Wegwerf-DB, inkl. echtem Gegenbuch-Lauf
mit SSH-signiertem lokalem Repo. Zusaetzlich nachgeholt, was der Tester
nicht herstellen konnte: vollstaendig unsigniertes Protokoll ->
kein_siegel statt der frueheren falschen Entwarnung nicht_noetig, und
seal-backlog nennt den fehlenden Schluessel als naechsten Schritt.
Gegenrichtung geprueft, R183-03 bleibt behoben.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -138,6 +138,45 @@ Sie verschwinden also nicht aus dem Bericht – sie zählen nur nicht mehr als
|
||||
offener Befund. Jede **neue** Lücke, jede veränderte oder entfernte Altzeile
|
||||
und jedes gebrochene Siegel lösen weiterhin sofort Alarm aus.
|
||||
|
||||
### Wenn sich die Siegelwurzel ändert: Alarm, und warum
|
||||
|
||||
Erneutes Siegeln **ersetzt** die Grundlage, gegen die Manipulation nachgewiesen
|
||||
wird. Wer eine Altzeile per Datenbankzugriff entfernt und danach neu siegelt,
|
||||
bekommt eine passende Wurzel und eine beglaubigte Lücke — und die Prüfung im
|
||||
CRM meldet wieder `valid: true`. Der Wechsel der Wurzel ist die einzige Spur
|
||||
davon, die eine Maschine sehen kann.
|
||||
|
||||
Deshalb ist er ein **Alarm** (exit 2), kein Hinweis. Vorher stand hier eine
|
||||
Zeile Prosa, während der Rückgabecode auf 0 blieb — also genau das Muster, das
|
||||
wir dem CRM selbst zweimal angekreidet haben.
|
||||
|
||||
Der Alarm nennt die alte und die neue Wurzel samt Blattzahl. Ein Sprung von
|
||||
`10 Blätter` auf `9 Blätter` sagt sofort, was passiert ist.
|
||||
|
||||
**War der Wechsel gewollt** (typisch: dein einmaliges Erstsiegeln), bestätigst
|
||||
du ihn mit der Wurzel, die der Alarm ausgibt:
|
||||
|
||||
```bash
|
||||
# in der .env
|
||||
PROD_SEAL_ACK=72062c88a8b6e2b53b30496b483885cd
|
||||
|
||||
docker compose up -d # ein Lauf – die neue Wurzel wird beglaubigt
|
||||
PROD_SEAL_ACK= # danach wieder leeren
|
||||
```
|
||||
|
||||
Bestätigt wird bewusst **nicht** mit `true`, sondern mit der Wurzel selbst.
|
||||
Ein versehentlich stehen gelassener Wert passt beim nächsten Wechsel nicht mehr
|
||||
und kann deshalb keinen weiteren Austausch stillschweigend durchwinken.
|
||||
|
||||
**War er nicht gewollt**, sieh im CRM nach: das Sicherheits-Ereignis
|
||||
`AUDIT_SEAL_CHANGED` (Einstellungen → Monitoring) und die CRITICAL-Zeile zu
|
||||
`/api/audit-logs/seal-backlog` im Audit-Protokoll nennen Konto, Zeitpunkt und
|
||||
den Befund, der vor dem Siegeln galt.
|
||||
|
||||
Im CRM selbst ist erneutes Siegeln zusätzlich gegatet: es verlangt
|
||||
`{"confirm":"RESEAL"}` statt `{"confirm":"SEAL"}` — ein Austausch der
|
||||
Beweisgrundlage soll nicht dasselbe Wort haben wie das Einrichten.
|
||||
|
||||
### Zugang einrichten (das brauchst du vorher)
|
||||
|
||||
Das Gegenbuch braucht ein **eigenes Benutzerkonto** im CRM – kein Token. Der
|
||||
@@ -442,7 +481,7 @@ Deshalb gilt jetzt:
|
||||
|---|---|
|
||||
| 0 | alles in Ordnung, Checkpoint angehängt (bzw. Prüfung bestanden) |
|
||||
| 1 | Betriebsfehler (Konfiguration, Commit oder Push fehlgeschlagen) |
|
||||
| 2 | **Befund** – Widerspruch zwischen CRM und Gegenbuch, oder ungültige Signatur |
|
||||
| 2 | **Befund** – Widerspruch zwischen CRM und Gegenbuch, ungültige Signatur, oder die Wurzel des Bestandssiegels hat sich geändert (`NOTARY_SEAL_ACK`) |
|
||||
| 5 | **Anker unvollständig** – die Kette ist gültig, aber `refs/notary/seq-N` fehlt. Reparierbar durch einen Notar-Schreiblauf |
|
||||
| 4 | **Wächter-Gedächtnis fehlt** – Erstinbetriebnahme unbestätigt, oder Speicher nach der Etablierung verloren |
|
||||
| 3 | beglaubigter Stand nicht abschließend feststellbar – Remote fehlt/unerreichbar, erste Beobachtung, Zurückspulen nicht ausschließbar, **oder** Checkpoint erstellt aber nicht verankert |
|
||||
@@ -491,6 +530,8 @@ an, bis er geklärt ist.
|
||||
| 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 |
|
||||
| **Altzeile gelöscht und neu gesiegelt** (Wäsche) | **Wurzel des Bestandssiegels hat gewechselt – `valid` allein bleibt dabei `true`** |
|
||||
| Erstmals gesiegelt, ohne dass es jemand veranlasst hat | vorher keine Wurzel beglaubigt, jetzt eine |
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user