Sicherheit
Security Hardening v1.5.1
Umfassende Sicherheitshärtung des gesamten GateControl-Projekts. Basierend auf einem vollständigen Security-Audit mit 39 identifizierten Issues über 4 Bereiche: Authentication, Input Validation, Docker/Infrastructure und Frontend.
Übersicht
| Schwere | Gefunden | Behoben | By Design |
|---|---|---|---|
| CRITICAL | 6 | 6 | — |
| HIGH | 12 | 12 | — |
| MEDIUM | 11 | 11 | — |
| LOW/INFO | 10 | 7 | 3 |
| Gesamt | 39 | 36 | 3 |
CRITICAL Fixes
- #1 — Prototype Pollution CSRF-Bypass:
req.tokenAuth,req.tokenIdundreq.tokenScopeswerden defensiv zurückgesetzt inrequireAuth(). - #2 — Route-Auth Forward-Auth ohne Header:
/route-auth/verifygibt401 Unauthorizedzurück wennx-route-domainfehlt. - #3 — Caddy Config Injection: Header-Namen/Werte validiert,
rate_limit_windowAllowlist,sticky_cookie_nameRegex. - #4 — DNS-Check SSRF: Domain-Validierung vor DNS-Lookup, aufgelöste IPs nicht in Response.
- #5 — Node als Root: Bewusst beibehalten — WireGuard CLI erfordert root, Container-Isolation ist Sicherheitsgrenze.
- #6 — Key-File Permissions: Nach
chown -Rwerden Secret-Files aufroot:rootmitchmod 600zurückgesetzt.
HIGH Fixes
- #7 — Route-Auth Lockout: Von IP-basiert auf Email-basiert geändert (verhindert IP-Rotation-Bypass).
- #8 — OTP Range: Vollständiger Bereich 000000–999999 mit
padStart. - #9 — OTP Resend: Erfordert gültige pending 2FA-Session.
- #10 — Route-Auth CSRF-Key: Eigener HMAC-abgeleiteter Key statt geteiltem App-Secret.
- #11 — WireGuard Config Injection: DNS als IP-Liste validiert, Keepalive als Integer, Newlines blockiert.
- #12 — Email HTML Injection: Alle interpolierten Werte in Email-Templates escaped.
- #13 — Route Target SSRF: Private/Loopback-IPs als direkte Route-Targets blockiert (Peer-verlinkte Routes nicht betroffen).
- #14 — Metrics Token Leak:
?token=Query-Parameter entfernt, nur Header-Auth. - #15 — WG Key in Logs: wg-quick Output gefiltert.
- #16 — Trust Proxy: Auf Loopback eingeschränkt.
- #17 — CSP Styles: Aufgeteilt in
style-src-elem(nonce) undstyle-src-attr(inline). - #18 — Dashboard XSS: API-Integers mit
parseIntundtextContent.
MEDIUM Fixes
- #19 — TOTP Replay-Prevention: In-Memory Tracking benutzter Codes (90s Expiry).
- #20 — Session Secure Warning: Warnung bei Production ohne HTTPS.
- #21 — Rate-Limiter Bypass: Erhöhtes Limit nur für Session-Auth.
- #22 — Backup Key Validation: Regex-Allowlist für Settings-Keys.
- #23 — IP Filter Fix:
req.ipstatt Raw X-Forwarded-For. - #24 — CSS Injection: Peer-Group Color gegen Hex-Regex validiert.
- #25 — Monitoring XSS: Response Time mit
parseIntsanitized. - #26 — API Key Masking: ip2location Key nicht im DOM, zeigt "Key is set".
- #27 — Health Endpoint: Details nur für localhost.
- #28 — WG Signal Handling: Guard-Variable verhindert Race Condition.
LOW/INFO Fixes
| # | Fix |
|---|---|
| #30 | Rate-Limit Error-Strings i18n (EN+DE) |
| #31 | Hardcoded German Strings durch i18n ersetzt |
| #32 | Dead Code entfernt |
| #33 | Argon2 Parallelism reduziert (4 → 1) |
| #37 | frame-ancestors: 'self' in CSP |
| #38 | Crypto Split mit Längenprüfung |
| #39 | Branding-Felder: max 255/2000 Zeichen |
Funktionsbeeinträchtigungen (Breaking Changes)
#13 — Route-Targets: Private IPs blockiert
Direkte Eingabe von privaten oder Loopback-IP-Adressen als Route-Target ist blockiert. Betroffen sind: 127.x, 10.x, 172.16-31.x, 192.168.x, 169.254.x, 0.x.
Nicht betroffen: Routes mit Peer-Verlinkung. Die WireGuard-Peer-IP wird weiterhin akzeptiert.
Migration: Erstelle einen WireGuard-Peer für das Zielgerät und wähle den Peer im Route-Dropdown statt die IP manuell einzugeben.
#14 — Prometheus: Query-Parameter-Auth entfernt
?token=gc_xxx wird nicht mehr akzeptiert. Nur Header-Auth:
scrape_configs:
- job_name: 'gatecontrol'
metrics_path: '/metrics'
authorization:
type: 'Bearer'
credentials: 'gc_abc123...'
static_configs:
- targets: ['gatecontrol.example.com:443']
scheme: https
Alternative: credentials_file für noch mehr Sicherheit.
Weitere Breaking Changes
| Fix | Auswirkung |
|---|---|
| #7 | Lockout per Email statt IP |
| #10 | Route-Auth CSRF-Tokens nach Update einmalig ungültig (Seite neu laden) |
| #16 | Nur Loopback als Proxy vertraut |
| #26 | ip2location API-Key nicht mehr sichtbar im UI |
| #27 | /health extern nur noch {ok: true/false} |