Pentest R87: Identifier-Whitelist vor stripHtml ziehen

R87.1 LOW: stripHtml lief im R86-Fix VOR der Whitelist.
`<b>bold</b>` ging als `"bold"` mit 200 OK durch,
`<script>…</script>` reduzierte auf leeren String → null in DB
→ vorheriger Wert ohne Fehlermeldung überschrieben.

Fix: validateContractIdentifier läuft jetzt direkt gegen den
Raw-Input für die fünf Identifier-Felder. Die strikte Whitelist
lehnt eh alles ab, was stripHtml normalerweise auffangen würde
(Tags, Schemes, Zero-Width, Homoglyphe, Percent-Encoding) –
Defense-in-Depth bleibt, nur ehrlich (400 statt silent-200).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 12:50:45 +02:00
parent c8b86ca9a7
commit 26959ec909
3 changed files with 64 additions and 3 deletions
+38
View File
@@ -537,6 +537,44 @@ erneut als „offenes Finding" auftaucht.
---
## 🔒 Runde 87 Whitelist vor Sanitizer (silent-mutation-Schutz)
**Finding (LOW): Sanitizer-Order maskiert Tag-Verstöße**
Im ursprünglichen R86-Fix lief `stripHtml(body)` **vor**
`validateContractIdentifier`. Das hatte einen subtilen Bypass:
| Payload | Status | Tatsächlich gespeichert |
|--------------------------------------|------------|-------------------------|
| `<b>bold</b>` | 200 OK | `"bold"` (silent strip) |
| `EVN<b>2024</b>` | 200 OK | `"EVN2024"` |
| `<script>alert(1)</script>` | **200 OK** | `null` **vorherigen Wert überschrieben** |
| `foo<bar>baz` | 200 OK | `"foobarbaz"` |
Kein direkter XSS-Vektor (React + DB-Whitelist greifen weiterhin),
aber zwei reale UX-/Datenintegritäts-Risiken:
1. Admin tippt `VG<2024>001`, bekommt 200 zurück, gespeichert ist
`VG2024001` ohne Hinweis auf die Mutation.
2. Werte die komplett aus Tags bestehen (`<script>…</script>`)
werden vom Sanitizer auf den leeren String reduziert →
`null` in der DB → **vorheriger Wert wird stillschweigend
gelöscht**.
**Fix:** Validierungs-Reihenfolge für die fünf Identifier-Felder
umgedreht `validateContractIdentifier` läuft jetzt **direkt
gegen den Raw-Input**, ohne dass `stripHtml` ihn vorher
glättet. Die strikte Whitelist
`^[A-Za-z0-9_\-/. ]{0,100}$` lehnt sowieso alles ab, was
`stripHtml` normalerweise abgefangen hätte (Tags, Schemes,
Zero-Width-Chars, Homoglyphe, Percent-Encoding) Defense-in-
Depth bleibt unverändert, nur jetzt ehrlich (400 statt silent-200).
Single-Line-Patch in [`backend/src/controllers/contract.controller.ts`](../backend/src/controllers/contract.controller.ts)
`sanitizeContractBody`.
---
## 🧭 Wann ist „dicht" dicht?
100 % gibt es nicht. Erreicht ist:
+14
View File
@@ -97,6 +97,20 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
## ✅ Erledigt
- [x] **🔒 Pentest R87 Whitelist vor Sanitizer (silent-mutation-Schutz)**
- R87.1 (LOW): `stripHtml` lief im R86-Fix VOR der Whitelist.
Tags wurden still weggestrippt → 200 OK mit mutierten Werten,
`<script>…</script>` reduzierte auf leeren String → `null` in
der DB → vorheriger Wert ohne Fehlermeldung überschrieben.
- Fix: Validierungs-Reihenfolge für die fünf Identifier-Felder
umgedreht `validateContractIdentifier` läuft jetzt direkt
gegen den Raw-Input. Die strikte Whitelist lehnt eh alles
ab, was stripHtml normalerweise auffangen würde (Tags,
Schemes, Zero-Width, Homoglyphe, Percent-Encoding) Defense-
in-Depth bleibt, nur ehrlich (400 statt silent-200).
- Single-Line-Patch in `contract.controller.ts`, Doku in
`SECURITY-HARDENING.md § Runde 87`.
- [x] **🔒 Pentest R86 Vertrags-Identifier härten**
- R86.1 (LOW): >999-Zeichen-Strings auf Kunden-/Vertrags-/
Auftragsnummer warfen 500 (DB-Overflow `VARCHAR(191)`) statt 400.