Security-Hardening Runde 12: Information-Disclosure + Input-Validation
Pentest Runde 7 (Anschlussrunde):
MEDIUM – Interne Felder in Portal-Responses:
- sanitizeCustomerStrict strippt zusätzlich portalTokenInvalidatedAt,
portalLastLogin, portalPasswordMustChange, lastBirthdayGreetingYear,
privacyPolicyPath, businessRegistrationPath, commercialRegisterPath.
- Neue sanitizeContract/Strict + sanitizeContracts/Strict: entfernt
portalPasswordEncrypted immer (nur über /password-Endpoint mit Audit
abrufbar), für Portal-User zusätzlich commission/notes/nextReviewDate.
- getContract + getContracts wählen je nach isCustomerPortal die
passende Variante. Mitarbeiter sehen commission/notes weiterhin.
LOW – Integer-Truncation bei IDs:
parseInt('6abc') → 6 lief vorher durch. Neue Heuristik-Middleware
unter /api: jedes Pfad-Segment, das mit Ziffer beginnt aber nicht
aus reinen Ziffern besteht, wird mit 400 abgelehnt. Trifft alle
Sub-Router ohne dass jede Route einzeln angefasst werden muss.
INFO – Rate-Limit: Code-Stand limit=10 für Login, limit=5 für
Password-Reset (lokal verifiziert: 11. failed login = 429). Pentester
sah vermutlich noch älteren Build. Kein Code-Change.
Live-verifiziert:
- /customers/6abc → 400 "Ungültige ID im URL-Pfad"
- /customers/3 → 200, /contracts/1abc/history → 400, normale Pfade OK
- Portal-User /customers/3: keine portalLastLogin/portalPasswordMustChange/
portalTokenInvalidatedAt/etc. mehr in Response
- Portal-User /contracts/15: keine commission/notes/portalPasswordEncrypted/
nextReviewDate
- Admin /contracts/15: commission/notes/nextReviewDate sichtbar,
portalPasswordEncrypted weg
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,41 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
||||
|
||||
## ✅ Erledigt
|
||||
|
||||
- [x] **🚨 Pentest Runde 7 (Anschlussrunde) – Information-Disclosure + Input-Validation**
|
||||
- **MEDIUM – Interne Felder in Portal-Responses**:
|
||||
* `sanitizeCustomerStrict` strippt jetzt zusätzlich
|
||||
`portalTokenInvalidatedAt`, `portalLastLogin`,
|
||||
`portalPasswordMustChange`, `lastBirthdayGreetingYear`,
|
||||
`privacyPolicyPath`, `businessRegistrationPath`,
|
||||
`commercialRegisterPath`.
|
||||
* Neue `sanitizeContract` / `sanitizeContractStrict` /
|
||||
`sanitizeContracts(Strict)`: entfernt
|
||||
`portalPasswordEncrypted` (immer; ist nur über den dedizierten
|
||||
`/password`-Endpoint mit Audit-Log abrufbar) und für Portal-
|
||||
User zusätzlich `commission`, `notes`, `nextReviewDate`.
|
||||
* `getContract` + `getContracts` rufen jetzt die passende
|
||||
Sanitize-Variante je nach `req.user.isCustomerPortal` auf;
|
||||
Mitarbeiter sehen weiterhin commission/notes (Admin-Workflow),
|
||||
nur `portalPasswordEncrypted` ist generell entfernt (Klartext
|
||||
nur über dedicated Endpoint).
|
||||
* Live-verifiziert: Portal sieht 0 Leaks, Admin sieht
|
||||
commission/notes weiterhin.
|
||||
- **LOW – Integer-Truncation bei IDs**:
|
||||
`parseInt('6abc')` → `6` hat alle Endpoints durchgewunken.
|
||||
Neuer middleware in `index.ts`: jedes URL-Pfad-Segment unter
|
||||
`/api`, das mit Ziffer beginnt aber nicht aus reinen Ziffern
|
||||
besteht, wird mit HTTP 400 abgelehnt. Heuristik trifft alle
|
||||
`/resource/<id>(\D+)`-Patterns ohne dass jeder einzelne
|
||||
Sub-Router angefasst werden muss.
|
||||
* Live-verifiziert: `/customers/6abc` → 400 mit klarer Meldung,
|
||||
`/customers/3` weiterhin 200, `/contracts/1abc/history`
|
||||
→ 400, normaler Pfade `/audit-logs/customer/3` → 200.
|
||||
- **INFO – Login-Rate-Limit „nach 6 nicht aktiv"**:
|
||||
Code-Stand `limit: 10` für `loginRateLimiter`, lokal verifiziert:
|
||||
11. Versuch = 429. Pentester sah vermutlich noch alten Build
|
||||
oder eine andere Lokation (PW-Reset hat `limit: 5`). Kein
|
||||
Code-Change.
|
||||
|
||||
- [x] **🛠 Rate-Limit-Sperren: Admin-UI zum Freigeben**
|
||||
- Bei einer Pentest-Runde hat der Tester sich selbst durch zu viele
|
||||
Login-Versuche ausgesperrt → ohne Container-Restart kein Weg zurück.
|
||||
|
||||
Reference in New Issue
Block a user