Security-Hardening Runde 11: Pentest Runde 7 (Portal-PW + Download-Tokens)
Hit-List vom Pentester abgearbeitet. Hauptpunkte:
1) Contract/Mail-Credentials (password/internet/sip/simcard, mailbox/send/
reset-password): ALLE bereits durch canAccess* gesichert, keine Lücke.
2) GET /customers/:id/portal/password (Klartext-Portal-PW-Abruf):
fehlender canAccessCustomer-Check ergänzt. Defense in depth gegen
versehentliche customers:update-Permission an Portal/eingeschränkte
Mitarbeiter.
3) Admin-Endpoints (factory-reset, developer/*, audit-logs/rehash,
audit-logs/customer): durch admin-Permissions geschützt – Portal-User
haben diese nicht.
4) Token-in-URL (NIEDRIG): Langlebige Access-JWTs landeten als ?token= in
URLs für iframe-PDFs, Audit-Export-Window etc. → nginx-Logs +
Browser-History + Referer.
Lösung: kurzlebige Download-Tokens.
- signDownloadToken() liefert JWT mit type='download', exp=60s
- Auth-Middleware akzeptiert type='download' AUSSCHLIESSLICH via
?token=, niemals als Bearer-Header
- POST /api/auth/download-token Endpoint (authenticated)
- Frontend: authApi.getDownloadToken() utility
- 4 Stellen migriert: AuditLog-Export, PdfTemplate-Preview-iframe,
PdfTemplate-Generate, ContractDetail-PDF-Generate (2x),
Portal-Privacy-PDF
- fileUrl/getAttachmentUrl sind synchron + breit gestreut – Migration
bleibt für Folge-PR
Live-verifiziert:
- Download-Token: 1773 Zeichen, type=download, exp-iat=60s
- als Header → 401 (Falscher Token-Typ), als ?token= → 200
- portal-user (Customer 3) auf customers/2/portal/password → 403
Rate-Limiter-Check: express-rate-limit Fixed-Window, kein Reset bei jedem
Request (Pentester-Klage „Fenster reseted sich" stimmt mit dem Code nicht
überein – wahrscheinlich Retry-After-Misinterpretation). Kein Code-Bug
identifiziert; ggf. später Admin-Override-Endpoint nachrüsten.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -97,6 +97,37 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
|
||||
|
||||
## ✅ Erledigt
|
||||
|
||||
- [x] **🚨 Pentest Runde 7 – Hit-List durchgegangen + kurzlebige Download-Tokens**
|
||||
- **Credential-Endpoints** (Contracts password/internet/sip/simcard +
|
||||
Stressfrei mailbox/send/reset-password): ALLE bereits durch
|
||||
`canAccessContract`/`canAccessStressfreiEmail` gesichert – keine
|
||||
Lücke gefunden.
|
||||
- **`GET /customers/:id/portal/password`** (Klartext-Portal-Passwort-
|
||||
Abruf): hatte KEINEN `canAccessCustomer`-Check. Fix: eingefügt.
|
||||
Defense in depth gegen versehentlich falsch vergebene
|
||||
`customers:update`-Permission.
|
||||
- **Admin-Funktionen** (factory-reset, developer/*, audit-logs/rehash,
|
||||
audit-logs/customer): alle durch admin-level Permissions
|
||||
(`settings:update`, `developer:access`, `audit:admin`, `audit:read`)
|
||||
geschützt – Portal-User haben diese nicht.
|
||||
- **Token-in-URL (NIEDRIG)**: Langlebige Access-JWTs landeten als
|
||||
`?token=` in URLs für PDF-iframe, Audit-Log-Export, PDF-Generate
|
||||
und Portal-Privacy-PDF → nginx-Access-Logs, Browser-History,
|
||||
Referer-Header.
|
||||
* **Neuer Mechanismus**: `POST /api/auth/download-token` liefert
|
||||
ein kurzlebiges JWT mit `type: 'download'` und `exp: 60s`.
|
||||
* Auth-Middleware akzeptiert `type: 'download'` AUSSCHLIESSLICH
|
||||
via `?token=` Query, niemals als Bearer-Header. So kann ein in
|
||||
Logs geleaktes Download-Token nicht für reguläre API-Aufrufe
|
||||
missbraucht werden.
|
||||
* Frontend-Migration: 4 Stellen umgestellt (Audit-Log-Export,
|
||||
PDF-Template-Preview, PDF-Generate von ContractDetail + Modal,
|
||||
Portal-Privacy-PDF). `fileUrl` und `getAttachmentUrl` sind
|
||||
synchron und in vielen Components verstreut – Migration dieser
|
||||
bleibt als Folge-Aufgabe.
|
||||
* Live-verifiziert: Download-Token = 1773 Zeichen, type=download,
|
||||
exp-iat=60s, als Header → 401, als ?token= → 200.
|
||||
|
||||
- [x] **🚨 Pentest Runde 6 – Sammelfix + Strukturelles Audit (8 Findings + Audit-Sweep)**
|
||||
- **KRITISCH-01 `GET /emails/:id/thread`**: kein Owner-Check →
|
||||
Portal-Kunde konnte alle Mail-Threads durchsuchen. Fix:
|
||||
|
||||
Reference in New Issue
Block a user