Security-Hardening Runde 7: SSRF-Schutz + Logout-Endpoint
🛡 SSRF-Schutz in test-connection / test-mail-access - Admin-User konnte über apiUrl bzw. SMTP/IMAP-Server-Felder Connections zu Cloud-Metadata-Endpoints (169.254.169.254, metadata.google.internal etc.) auslösen. Internal-Port-Scan über Timing-Differenzen war messbar. - Fix: neuer utils/ssrfGuard.ts blockiert pre-flight 169.254.0.0/16, 0.0.0.0/8, Multicast/Reserved-Ranges, AWS-IPv6-Metadata, IPv6-Link-Local und Cloud-Metadata-Hostnames. Loopback (127.0.0.0/8) bleibt erlaubt – legitime Plesk/Postfix- Setups sollen weiter funktionieren. 🔒 Logout-Endpoint POST /api/auth/logout - Setzt tokenInvalidatedAt / portalTokenInvalidatedAt auf jetzt. Auth-Middleware prüft das Feld bereits und lehnt Tokens mit iat davor ab. Ohne diesen Endpoint blieb ein "abgemeldeter" JWT bis Expiry (7d) gültig. Live-verifiziert: - 169.254.169.254 / metadata.google.internal / 0.0.0.0 → 400 - 127.0.0.1 (Plesk-Fall) weiter erlaubt - /me vor Logout 200, nach Logout 401 "Sitzung ungültig" Geprüft + sauber (Runde 7, kein Bug): - Public Consent (122-bit Random-UUID nicht brute-force-bar) - Magic-Bytes-Bypass beim Upload - PDF manualValues Injection (keine HTML-Render-Surface) - Query-Filter-Override (?customerId=X) – vom Portal-Filter ignoriert - Audit-Logs / Email-Config / Backup-Endpoints als Portal: 403 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -141,6 +141,40 @@ 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 7 – Letzter Schliff (SSRF + Logout):**
|
||||
- **SSRF-Schutz** in `test-connection` und `test-mail-access`: ein
|
||||
Admin-User konnte über die Plesk-API-URL bzw. SMTP/IMAP-Server-Felder
|
||||
Connections zu beliebigen IPs auslösen (Cloud-Metadata-Endpoints,
|
||||
Link-Local, AWS/GCP-Metadata-Hosts). Internal-Port-Scanning via
|
||||
Timing-Differenzen war messbar (22/80/3306/5432/6379 unterschiedlich).
|
||||
Fix: neuer Helper `utils/ssrfGuard.ts` blockiert vor jeder ausgehenden
|
||||
Verbindung 169.254.0.0/16, 0.0.0.0/8, Multicast/Reserved-Ranges,
|
||||
AWS-IPv6-Metadata, IPv6-Link-Local und bekannte Cloud-Metadata-
|
||||
Hostnames (metadata.google.internal etc.). Loopback (127.0.0.0/8)
|
||||
bleibt erlaubt für legitime Plesk/Postfix-Setups.
|
||||
- **Logout-Endpoint** `POST /api/auth/logout`: setzt
|
||||
`tokenInvalidatedAt` / `portalTokenInvalidatedAt` auf jetzt. Auth-
|
||||
Middleware prüft das Feld und lehnt Tokens mit `iat` davor ab.
|
||||
JWTs sind stateless – ohne diesen Mechanismus bleibt ein
|
||||
„abgemeldeter" Token bis zum natürlichen Expiry (7d) gültig.
|
||||
- Live-verifiziert: 169.254.169.254/metadata.google.internal/0.0.0.0
|
||||
werden mit 400 abgelehnt; 127.0.0.1 weiter erlaubt; Logout
|
||||
invalidiert den Token sofort (HTTP 401 „Sitzung ungültig").
|
||||
|
||||
**Geprüft + sauber (Runde 7):**
|
||||
- Public Consent (random Hash → 404, kein Brute-Force durch 122-bit-UUID)
|
||||
- Magic-Bytes-Bypass beim Upload (HTML als image/png) → blockiert
|
||||
- PDF-Generation mit injizierten manualValues → kein XSS-Vektor (PDFs sind keine Web-Renderer)
|
||||
- Audit-Logs für Portal-User: 403
|
||||
- Email-Config-Update als Portal: 403
|
||||
- Backup-Endpoints als Portal: 403
|
||||
- Query-Filter-Override (?customerId=X) → vom Portal-Filter ignoriert
|
||||
|
||||
**Bewusst NICHT gefixt (zu invasiv für v1.0):**
|
||||
- Vollständige DNS-Resolution beim SSRF-Guard (gegen DNS-Rebinding) –
|
||||
kann legitimes CDN/Caching brechen, v1.1-Item.
|
||||
- Per-File-Ownership-Check bei `/api/uploads` (siehe Runde 5).
|
||||
|
||||
- **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
|
||||
|
||||
Reference in New Issue
Block a user