Files
breadcrumb-the-shire/docs/anfragelimits.md
fs 25370a1a55 add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks
- Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists
- Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services
- Add Microsoft OIDC SSO, API token management, and user lifecycle features
- Add swagger-ui vendor integration and OpenAPI spec
- Add production Docker setup and bin/ scripts
- Update composer dependencies, config, templates, and frontend assets throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 15:27:35 +01:00

120 lines
3.3 KiB
Markdown

# 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: `<selector>|<ip>`
- Limit: `300` Hits pro `60` Sekunden
- Block: `120` Sekunden
2. Kein Bearer (`ip-only`)
- Scope: `api.ip`
- Key: `<ip>`
- Limit: `120` Hits pro `60` Sekunden
- Block: `120` Sekunden
Bei überschreitung:
- Response: `429`
- Body: `{"error":"rate_limit_exceeded"}`
- Header: `Retry-After: <sekunden>`
## Login Rate Limiting
Integration:
- `/pages/auth/login().php`
Aktuelle Limits:
1. Schritt `resolve_email` (IP)
- Scope: `login.resolve.ip`
- Key: `<ip>`
- Limit: `25` Hits pro `600` Sekunden
- Block: `300` Sekunden
2. Schritt `login_password` (email + ip)
- Scope: `login.password.email_ip`
- Key: `<lowercase-email>|<ip>`
- 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).