Security-Hardening Runde 6: Customer-Liste-Leak + XFF-Bypass + Vollmacht-Validation
Tiefer Live-Pentest deckte 3 weitere Schwachstellen: 🚨 CRITICAL: GET /api/customers leakte komplette Kundendatenbank - Stage-4 hatte canAccessCustomer auf den Single-Endpoint angewendet, der List-Endpoint hatte nur den Daten-Sanitizer (filtert Passwort-Hashes) aber keinen Portal-Filter. Folge: Portal-Kunde sah ALLE Kunden mit Namen, E-Mails, customerNumber etc. – DSGVO-relevant. - Fix: getCustomers filtert für Portal-User auf eigene + vertretene IDs. 🚨 HIGH: Rate-Limit-Bypass via X-Forwarded-For - `trust proxy = 1` hat jedem XFF-Wert vertraut. 12+ Logins mit rotierender XFF-IP gingen ohne 429 durch. - Fix: `trust proxy = 'loopback'` – XFF nur noch von 127.0.0.1 / ::1 akzeptiert (= lokaler Reverse-Proxy). - Plus: LISTEN_ADDR-Default 127.0.0.1 in Production, damit das Backend nicht von außen direkt ansprechbar ist. 🛡 MEDIUM: Self-Grant + Existence-Disclosure in toggleMyAuthorization - Portal-User konnte: a) sich selbst Vollmacht erteilen (customerId=representativeId=1) b) Authorization-Records für nicht-existierende customerIds anlegen (scheitert erst am DB-Constraint mit vollem Prisma-Stack-Leak) c) Customer-IDs durch 404-vs-403-Differenzen enumerieren. - Fix: Self-Grant 400. Existenz + aktive CustomerRepresentative-Beziehung in einem Query – Non-Existent / Non-Related geben identisch 403. Prisma-Error-Stacks generisch ersetzt. Live-verifiziert: Customer-Liste filtert, Self-Grant 400, Existence-Probing dicht. Geprüft + sauber (Runde 6, kein Bug): - Prototype Pollution Login-Body - HTTP-Method-Override-Header - Path-Traversal Backup-Name (Regex blockt) - Developer-Routes existieren nicht - Email-Endpoints mit fremder StressfreiEmail-ID → 403 - /api/customers/:id GET liefert 403 statt 404 (kein Leak) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -141,6 +141,47 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
||||
- Provider/Tariff-GETs: `requirePermission('providers:read')` (Portal-Kunden sehen Provider-Liste nicht mehr)
|
||||
- SMTP-Header-Injection: zentrale CRLF-Validierung in `smtpService.sendEmail` (schützt alle Caller)
|
||||
- bcrypt cost 10 → 12 (OWASP 2026)
|
||||
- **Runde 6 – Tiefer Live-Pentest (auf Wunsch des Users, „bevor andere es tun"):**
|
||||
- 🚨 **`GET /api/customers` leakte als Portal-User die komplette
|
||||
Kundendatenbank** (alle Namen, E-Mails, customerNumber etc.). Der
|
||||
Single-Endpoint war Stage 4 mit `canAccessCustomer` gefixt, der List-
|
||||
Endpoint nicht. Jetzt: Portal-User bekommt nur eigene + vertretene
|
||||
Kunden (Filter im Controller).
|
||||
- 🚨 **Rate-Limit-Bypass via `X-Forwarded-For`**: 12+ Login-Versuche
|
||||
mit rotierenden XFF-Werten gingen alle durch ohne 429. `trust proxy = 1`
|
||||
hat naiv jedem XFF-Wert vertraut. Jetzt: `trust proxy = 'loopback'` –
|
||||
XFF wird nur akzeptiert wenn die Connection von 127.0.0.1 / ::1 kommt
|
||||
(= lokaler Reverse-Proxy). Plus: `LISTEN_ADDR=127.0.0.1` in Production-
|
||||
Default, damit das Backend nicht direkt von außen ansprechbar ist.
|
||||
- **Self-Grant + Existence-Disclosure in `toggleMyAuthorization`**:
|
||||
- Portal-User konnte sich selbst Vollmacht erteilen (1→1) und
|
||||
Datensätze für beliebige `representativeId`s anlegen (auch nicht-
|
||||
existierende, scheiterte erst auf DB-Constraint mit Prisma-Stack-Leak).
|
||||
- 404 vs 403 erlaubte Existence-Probing der gesamten customer-ID-Range.
|
||||
- Fix: Self-Grant 400er. Existenz + aktives `CustomerRepresentative`-
|
||||
Verhältnis in einem Query, beide Fehlfälle identisch 403.
|
||||
- **Prisma-Error-Leak generisch in `toggleMyAuthorization`**: keine
|
||||
Prisma-Stacks mehr im Response.
|
||||
- Live-verifiziert: Customer-Liste 3 statt 3000 (jetzt nur erlaubte),
|
||||
Self-Grant 400, Existence-Disclosure dicht (alle 403 uniform), Auth
|
||||
auf `/api/customers/:id` 200/403 (kein 404-Leak).
|
||||
|
||||
**Geprüft + sauber (Runde 6):**
|
||||
- Prototype Pollution beim Login → kein Effekt
|
||||
- HTTP-Method-Override via Header → ignoriert
|
||||
- Path-Traversal in Backup-Name → durch Regex blockiert
|
||||
- Developer-Routes existieren nicht (404)
|
||||
- Email-Endpoints (Send/Sync/Read mit fremder StressfreiEmail-ID) → 403
|
||||
- Self-grant Vollmacht via `customers/X/representatives` → 403 (perm)
|
||||
- `/api/customers/:id` GET: 200 für eigene, 403 sonst (kein 404-Leak)
|
||||
|
||||
**Offen für v1.1:**
|
||||
- `/api/contracts/:id` GET liefert 404 für nicht-existente IDs (Existence-
|
||||
Probing). Da contractIds aber nicht direkt mit personenbezogenen Daten
|
||||
korrelieren, niedrig-Prio. Vereinheitlichung auf 403 wäre sauberer.
|
||||
- Prisma-Error-Leaks in anderen Admin-Endpoints (z.B. `addInvoice` bei
|
||||
Validation-Fehler) – Defense-in-Depth-Kandidat.
|
||||
|
||||
- **Runde 5 – Hack-Das-Ding-Audit (Live-Pentest + 3 parallele Audit-Agents):**
|
||||
- 🚨 **`/api/uploads/*` war OHNE AUTH erreichbar** (DSGVO-GAU!) – jetzt hinter
|
||||
`authenticate`. Direkte <a href>-Links nutzen `?token=...` Query-Parameter,
|
||||
|
||||
Reference in New Issue
Block a user