Refresh-Fehlschlag: Detection-Gap geschlossen (Pentest R164-01)

Fehlgeschlagener /auth/refresh (Replay/Brute-Force auf geraubte
Refresh-Tokens) wurde als TOKEN_REFRESH/LOW geloggt und entging der
Alarmierung - ein Angreifer konnte von /login auf /refresh ausweichen,
um unter der LOGIN_FAILED-Schwelle zu bleiben.

Audit-Actions speisen die Alert-Engine nicht (die zaehlt SecurityEvent
via emit). Fix daher an zwei Ebenen:

- Detection: refresh()-Catch emittiert TOKEN_REJECTED -> greift die
  bestehende Schwelle (>=3 TOKEN_REJECTED/5min/IP -> CRITICAL). Severity
  wie Access-Token: abgelaufen/revoked = LOW (kein Sofort-Alert),
  ungueltige Signatur/Manipulation = HIGH. auth.service reicht dafuer
  err.code REFRESH_EXPIRED/REFRESH_INVALID durch. "Kein Cookie" emittiert
  bewusst nicht (normaler Erstbesuch).
- Audit-Triage: fehlgeschlagener Refresh -> Sensitivitaet HIGH statt LOW
  + Label "Token-Refresh abgelehnt". Action bleibt TOKEN_REFRESH
  (semantisch ein Refresh, kein Login).

Verifiziert: tsx-Test abgelaufen->LOW, manipuliert/garbage->HIGH; tsc gruen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 18:20:21 +02:00
co-authored by Claude Opus 4.8
parent de0d6bd817
commit d599eb3702
4 changed files with 59 additions and 7 deletions
+24
View File
@@ -97,6 +97,30 @@ isolierte Instanz (keine Multi-Tenancy im Code), Provisioning + Abrechnung
## ✅ Erledigt
- [x] **🛡️ Refresh-Fehlschlag: Detection-Gap geschlossen (Pentest R164-01)** (2026-08-18)
- Folgefund zum Entrauschen: `determineAction` gab `/auth/refresh` bedingungslos
`TOKEN_REFRESH`/LOW → ein **fehlgeschlagener** Refresh (Replay/Brute-Force auf
geraubte/geratene Refresh-Tokens) rutschte als LOW durch und entging der
Alarmierung (Angreifer weicht von `/login` auf `/refresh` aus, um unter
CRITICAL zu bleiben).
- **Wichtig:** Audit-Actions speisen die Alert-Engine NICHT (die zählt
`SecurityEvent`-Zeilen via `emit()`). Der Tester-Minimalvorschlag (Action →
`LOGIN_FAILED`) hätte also keinen Alert ausgelöst. Echter Fix an 2 Ebenen:
- **Detection:** `refresh()`-Catch emittiert jetzt `TOKEN_REJECTED`
greift die bestehende Schwelle `≥3 TOKEN_REJECTED/5min/IP → CRITICAL`
(securityAlert.service, kein Severity-Filter). Severity wie Access-Token:
abgelaufen/revoked → LOW (benigne, kein Sofort-Alert), ungültige
Signatur/Manipulation → HIGH (Sofort-Alert). `auth.service` reicht dafür
`err.code` REFRESH_EXPIRED/REFRESH_INVALID durch. „Kein Cookie" emittiert
NICHT (normaler Erstbesuch).
- **Audit-Triage:** fehlgeschlagener Refresh → Sensitivität HIGH statt LOW +
Label „Token-Refresh abgelehnt (ungültig/abgelaufen)". Action bleibt
bewusst `TOKEN_REFRESH` (semantisch ein Refresh, kein Login).
- Verifiziert: tsx-Test — abgelaufen→LOW, manipuliert/garbage→HIGH; `tsc` grün.
- Nebenbefund R164-02 (pre-existing, kein Commit von uns): Refresh-Rotation
bietet keinen Replay-Schutz (alter Token bis exp gültig), aber fail-closed
nach Logout. Ggf. später: Refresh-Token-Jti-Blacklist / One-Time-Use.
- [x] **🔇 Audit-Log: stiller Token-Refresh entrauscht (`TOKEN_REFRESH`)** (2026-08-18)
- Automatische `POST /auth/refresh`-Aufrufe (Interceptor bei 401 / nach
Seiten-Reload, da Access-Token nur im Speicher) wurden als `CREATE` /