# Anfragelimits (Rate Limiting)

Letzte Aktualisierung: 2026-03-06

## Ziel

Brute-Force und API-Spam begrenzen, ohne Verfügbarkeit zu gefährden.

## Technischer Kern

- Service: `lib/Service/Security/RateLimiterService.php`
- Repository: `lib/Repository/Security/RateLimitRepository.php`
- Storage: `request_rate_limits`
  - `scope`, `subject_hash`, `hits`, `window_started_at`, `blocked_until`

Wichtig:

- Subject wird gehasht (SHA-256), kein Klartext-Key.
- Verhalten ist fail-open bei Storage-Fehlern.

## API-Limits (global)

Durchgesetzt in `lib/Http/ApiBootstrap.php`:

1. IP-only (`api.ip`)
   - 120 Requests / 60s
   - Block 120s
2. Token+IP (`api.token_ip`)
   - 300 Requests / 60s
   - Block 120s

Bei Block:

- HTTP `429`
- `Retry-After`
- Body `{"error":"rate_limit_exceeded"}`

## Web-Login-Limits

In `pages/auth/login().php`:

1. Resolve-Phase pro IP (`login.resolve.ip`)
   - 25 Versuche / 600s
   - Block 300s
2. Passwortphase pro E-Mail+IP (`login.password.email_ip`)
   - 5 Fehlversuche / 900s
   - Block 900s

Erfolgreicher Passwort-Login setzt den Passwort-Scope zurück.

## API-Login-Limits

In `pages/api/v1/auth/login().php` (zusätzlich zu globalen API-Limits):

1. IP (`api.auth.login.ip`)
   - 10 Fehlversuche / 600s
   - Block 300s
2. E-Mail+IP (`api.auth.login.email_ip`)
   - 5 Fehlversuche / 900s
   - Block 900s

Erfolgreicher API-Login setzt `api.auth.login.email_ip` zurück.

## Smoke-Test

1. API ohne Token >120 Requests/min gegen `/api/v1/*` -> `429` erwartet.
2. Web-Login mit falschem Passwort >5 pro E-Mail+IP -> `429` erwartet.
3. API-Login mit falschen Credentials wiederholt -> `429` erwartet.
