Pentest R120: canAccess-403 zusätzlich tamper-evident auditieren

Der Retest deckte auf, dass abgewehrte Cross-Customer-Zugriffe
(canAccess*-403) nur im SecurityEvent-Monitoring-Stream landeten
(ACCESS_DENIED, /api/monitoring/events) – nicht im AuditLog, wo
der Pentester suchte. Der Monitoring-Stream ist zudem löschbar
(DELETE /api/monitoring/events) und nicht hash-verkettet.

emitAccessDenied schreibt jetzt zusätzlich einen tamper-evidenten
AuditLog-Eintrag (action READ, resourceType 'AccessDenied',
Sensitivity HIGH), der ein Clearen des Monitoring-Streams
überlebt und über die Hash-Kette manipulationssicher ist. Gilt
für alle canAccess*-403 (Contract + Customer, inkl. Vollmacht-
fehlt-Fall). Kein Enum-/Schema-Change: READ + distinktiver
resourceType, filterbar via searchAuditLogs.

Korrigiert damit auch meine frühere ungenaue Aussage „landet im
Audit" – vorher war das die falsche Tabelle.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-07-17 22:51:40 +02:00
co-authored by Claude Opus 4.7
parent 23c530be46
commit 42705145dd
4 changed files with 47 additions and 1 deletions
+13
View File
@@ -692,6 +692,19 @@ zunächst gegen den falschen Host getestet wurde
`kundencenter-stage.stressfrei-wechseln.de`). Auf dem korrekten
Staging-Host reproduzierte sich das Finding sofort.
**Nachtrag Zwei-Stream-Logging der Abwehr:** Beim Retest fiel auf, dass
der abgewehrte Zugriff zwar im **SecurityEvent**-Monitoring-Stream landet
(`ACCESS_DENIED`, MEDIUM, sichtbar unter
`GET /api/monitoring/events?type=ACCESS_DENIED`), aber **nicht** im
AuditLog dort hatte der Pentester zuerst gesucht. Der Monitoring-Stream
ist zudem über `DELETE /api/monitoring/events` löschbar und nicht
hash-verkettet. `emitAccessDenied` schreibt daher jetzt **zusätzlich**
einen tamper-evidenten AuditLog-Eintrag (`action: READ`,
`resourceType: 'AccessDenied'`, Sensitivity HIGH), der ein Clearen des
Monitoring-Streams überlebt und über die Hash-Kette manipulationssicher
ist. Gilt für alle `canAccess*`-403 (Contract + Customer, jeweils inkl.
Vollmacht-fehlt-Fall).
---
## 🔒 Runde 110 Mass-Assignment-Whitelist auf 7 Katalog-Endpunkten
+8
View File
@@ -117,6 +117,14 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
rendert Vertrags-Provider/Tarif-Namen, React escaped sie als
Text. Neue Writes werden zusätzlich per `sanitizeContractBody`
(stripHtml) entschärft; der Altwert stammt aus DB-Direkteingabe.
- Nachtrag (Retest): Der abgewehrte Zugriff wird nun in ZWEI Streams
protokolliert. Bisher nur `SecurityEvent` (`ACCESS_DENIED`, sichtbar
unter `/api/monitoring/events`, aber löschbar + nicht hash-verkettet)
der Pentester suchte im AuditLog und fand nichts. `emitAccessDenied`
schreibt jetzt zusätzlich einen tamper-evidenten AuditLog-Eintrag
(`action: READ`, `resourceType: 'AccessDenied'`, Sensitivity HIGH)
für alle `canAccess*`-403. Meine ursprüngliche Formulierung „landet
im Audit" war die falsche Tabelle jetzt stimmt sie.
- [x] **👁 Kundenansicht: Toggle „Deaktivierte Verträge anzeigen"**
- Der Vertragsbaum beim Kunden (`CustomerDetail` → Tab Verträge)