# Anfragelimits (Rate Limiting) Letzte Aktualisierung: 2026-02-21 Dieses Dokument beschreibt das aktuelle zentrale Rate-Limiting für Login und API. ## Ziel - Brute-Force auf Login reduzieren. - API-Missbrauch (Burst/Spam) abfangen. - Einheitliches Verhalten mit `429` + `Retry-After`. ## Technischer Aufbau ### Storage Tabelle: `request_rate_limits` Spalten: - `scope`: Technischer Bereich (z. B. `login.password.email_ip`) - `subject_hash`: SHA-256 Hash des Subjects (keine Klartexte) - `hits`: Treffer im aktuellen Fenster - `window_started_at`: Beginn des aktuellen Zeitfensters (UTC) - `blocked_until`: Block-Ende (UTC), `NULL` wenn nicht geblockt Schema: - Tabelle wird in `db/init/init.sql` mit angelegt. - Für Bestandsumgebungen sollte ein idempotentes SQL-Update bereitgestellt werden. ### Service/Repository - Service: `/lib/Service/Security/RateLimiterService.php` - Repository: `/lib/Repository/Security/RateLimitRepository.php` Der Service ist absichtlich **fail-open**: - Wenn DB/Storage fehlschlägt, wird kein harter Lock erzeugt. - Requests laufen weiter, um Verfügbarkeit nicht zu brechen. ## API Rate Limiting Integration: - `/lib/Http/ApiBootstrap.php` (vor Auth) Aktuelle Limits: 1. Bearer vorhanden (`token + ip`) - Scope: `api.token_ip` - Key: `|` - Limit: `300` Hits pro `60` Sekunden - Block: `120` Sekunden 2. Kein Bearer (`ip-only`) - Scope: `api.ip` - Key: `` - Limit: `120` Hits pro `60` Sekunden - Block: `120` Sekunden Bei überschreitung: - Response: `429` - Body: `{"error":"rate_limit_exceeded"}` - Header: `Retry-After: ` ## Login Rate Limiting Integration: - `/pages/auth/login().php` Aktuelle Limits: 1. Schritt `resolve_email` (IP) - Scope: `login.resolve.ip` - Key: `` - Limit: `25` Hits pro `600` Sekunden - Block: `300` Sekunden 2. Schritt `login_password` (email + ip) - Scope: `login.password.email_ip` - Key: `|` - Limit: `5` Fehlversuche pro `900` Sekunden - Block: `900` Sekunden Verhalten: - Bei Block: HTTP `429` + `Retry-After`. - UI zeigt generische Meldung: `Too many login attempts. Please wait and try again.` - Bei erfolgreichem Passwort-Login wird der Passwort-Scope für diesen Key zurückgesetzt. ## Datenfluss pro Request 1. Key bilden (`scope` + Subject) 2. Subject hashen (`sha256`) 3. Bestehenden Datensatz lesen 4. Falls `blocked_until > now`: direkt blocken 5. Fenster prüfen (`window_started_at + windowSeconds`) 6. Hits erhöhen 7. Bei `hits > max`: `blocked_until = now + blockSeconds` 8. Ergebnis liefern (`allowed`, `retry_after`) ## Operative Hinweise - Zeitbasis ist UTC (`gmdate`). - Tabelle kann wachsen; in V1 gibt es noch keinen automatischen Cleanup für alte Rate-Limit-Zeilen. - Bei Bedarf kann ein Wartungsjob alte/obsolete Zeilen periodisch löschen. ## Smoke-Test (manuell) API: 1. Schnell >120 Requests/min ohne Bearer gegen `/api/v1/*` 2. Erwartung: `429` mit `Retry-After` Login: 1. Mehrere falsche Passwortversuche für dieselbe Email+IP 2. Erwartung nach 5 Fehlversuchen: `429` und Sperre für 15 Minuten ## Erweiterungen (nächster Schritt) - Limits in Settings pflegbar machen (statt Hardcode). - Optional stricter Mode in Production (fail-closed nur für API). - Cleanup-Job für `request_rate_limits`. - Optional Logging/Stats für geblockte Requests (z. B. im Admin-Stats Modul).