Bestandssiegel: Altbestand gegen stille Aenderung gesichert (Pentest R171-02)

Bei Hash-Version 1 sind nur 7 von 24 Spalten gehasht. Ein DB-Schreibzugriff
konnte eine LOGIN_FAILED-Zeile auf success=1 setzen, das Label umschreiben und
errorMessage leeren - alles Nicht-Hash-Felder, Hash unveraendert - und /verify
meldete weiterhin valid=true. Ein Einbruchsversuch war unsichtbar in einen
Erfolg umschreibbar.

Rueckwirkend signieren geht nicht, ein Rehash waere die falsche Medizin.
Stattdessen ein einmaliges, nicht destruktives Bestandssiegel: je Altzeile ein
Blattwert, die Wurzel darueber in einem HMAC-signierten Marker.

Umgesetzt nach den vier Bedingungen aus dem Pentest:
1. Blaetter ueber den vollen Zeileninhalt inkl. id und hashVersion, nicht ueber
   den 7-Feld-V1-Hash - sonst lebte die Luecke im Siegel weiter.
2. Wurzel signiert (steht im Marker, der selbst V3/HMAC ist). Ohne
   AUDIT_HMAC_KEY wird das Siegeln abgelehnt.
3. Bereich fix auf [1 ... v3FromId-1] statt Live-Abfrage hashVersion < 3. Sonst
   haette ein Up-Flip der Grenzzeile sie aus der geprueften Menge gedraengt.
4. Pruefung je id: vorhanden, weiterhin Altbestand, Inhalt == Blatt, dazu
   Wurzelabgleich.

Neuer Endpunkt POST /audit-logs/seal-backlog (audit:admin, confirm SEAL).
/verify meldet den Siegelzustand im Klartext, auch wenn kein Siegel existiert.

Verifiziert in separater Wegwerf-DB mit Mischbestand (6xV1, 5xV2, 5xV3): ohne
Siegel ist der Angriff unsichtbar, mit Siegel wird er erkannt und die Zeile
benannt; der Up-Flip der Grenzzeile wird ebenfalls erkannt. Wegwerf-DB danach
geloescht, Dev-Daten unberuehrt. tsc + vite build gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-19 22:12:51 +02:00
co-authored by Claude Opus 5
parent 2b0af772a2
commit 2932598c98
6 changed files with 300 additions and 1 deletions
+36
View File
@@ -97,6 +97,42 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
## ✅ Erledigt
- [x] **🧿 Bestandssiegel: Altbestand gegen stille Aenderung gesichert (Pentest R171-02, HIGH)** (2026-08-18)
- Der Pentester hat den Fund live reproduziert: Bei Hash-Version 1 sind nur
7 von 24 Spalten gehasht. Ein DB-Schreibzugriff konnte eine
`LOGIN_FAILED`-Zeile auf `success=1` setzen, das Label auf
„…hat sich angemeldet“ aendern und `errorMessage` leeren **alles
Nicht-Hash-Felder, Hash unveraendert** → `/verify` meldete `valid=true`.
Ein Einbruchsversuch war unsichtbar in einen Erfolg umschreibbar.
- Rueckwirkend signieren geht nicht; ein Rehash waere die falsche Medizin.
Stattdessen **Bestandssiegel**: einmalig, nicht destruktiv, schreibt nichts
um. Je Altzeile ein Blattwert, die Wurzel darueber in einem HMAC-signierten
Marker.
- Umgesetzt exakt nach seinen vier Bedingungen:
1. **Blaetter ueber den vollen Zeileninhalt** (alle Spalten inkl. `id` und
`hashVersion`), NICHT ueber den 7-Feld-V1-Hash sonst lebte die Luecke
im Siegel weiter.
2. **Wurzel signiert** sie steht im Marker, der selbst Version 3 und
HMAC-signiert ist. Ohne `AUDIT_HMAC_KEY` wird das Siegeln abgelehnt.
3. **Bereich fix auf `[1 … v3FromId-1]`** gebunden statt per Live-Abfrage
`hashVersion < 3`. Sein Umgehungsweg: ein Up-Flip der Grenzzeile
(V1→V2, schluessellos nachrechenbar) haette sie sonst aus der geprueften
Menge gedraengt sie waere nur als harmlose Luecke erschienen.
4. **Pruefung je id**: vorhanden, weiterhin Altbestand, Inhalt == Blatt.
Zusaetzlich Wurzelabgleich, damit auch ein veraenderter Blattbestand
auffliegt.
- Naht geschlossen: Zeilen ab `v3FromId` MUESSEN laut Versionsgrenze V3
sein, es bleibt also keine ungesiegelte Zone dazwischen.
- Neuer Endpunkt `POST /audit-logs/seal-backlog` (`audit:admin`,
`{"confirm":"SEAL"}`). `/verify` meldet den Siegelzustand im Klartext
auch wenn **kein** Siegel existiert („Aenderungen daran waeren nicht
erkennbar“).
- Verifiziert in einer separaten Wegwerf-Datenbank mit realistischem
Mischbestand (6×V1, 5×V2, 5×V3): ohne Siegel ist der Angriff unsichtbar
(`valid=true`) mit Siegel wird er erkannt und die Zeile benannt (`[3]`);
sein Up-Flip der Grenzzeile wird ebenfalls erkannt (`[6]`). Wegwerf-DB
danach geloescht, Dev-Daten unberuehrt. `tsc` + `vite build` gruen.
- [x] **🔒 Manifest-Kanal abgesichert (Pentest R171-01, HIGH; R171-03, LOW)** (2026-08-18)
- **R171-01:** `verifyIntegrity` vertraute Loeschungs-Manifesten
bedingungslos ohne zu pruefen, ob die Traegerzeile ueberhaupt signiert