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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user