
# Einstellungen des Angriffsschutzes ersetzen

`PUT /v1/attack-protection` · Berechtigung `attack-protection:write`

Ersetzt das gesamte Angriffsschutz-Objekt (`application/json`). Weggelassene Abschnitte und Felder kehren zu ihren Standardwerten zurück. `If-Match` ist erforderlich: Fehlt es, gibt es `428`; ist das `ETag` veraltet, `412`. Dasselbe Objekt erneut zu schreiben, schreibt nichts und behält das `ETag` bei. Um wenige Felder zu ändern, verwenden Sie `PATCH`.

## Anfrage

| Feld | Typ | Pflicht | Beschreibung |
|---|---|---|---|
| `If-Match` | Header | ja | Das `ETag` des Objekts, wie zuletzt gelesen. Fehlt: `428`; veraltet: `412`. |
| Body | Objekt | ja | Das konfigurierte Objekt, höchstens 32 KiB. Jeder Abschnitt ist optional; jedes Feld darin ist ebenfalls optional. |

| Feld | Typ | Bereich oder Werte | Standard | Untergrenze |
|---|---|---|---|---|
| `brute_force.mode` | Text | `log`, `enforce` | `enforce` | |
| `brute_force.max_failures` | Ganzzahl | 5-100 | 20 | höchstens 100 |
| `brute_force.window_minutes` | Ganzzahl | 5-60 | 15 | |
| `brute_force.lock_minutes` | Ganzzahl | 5-60 | 15 | mindestens 5 |
| `brute_force.notify_user` | Wahrheitswert | | `true` | |
| `suspicious_ip_throttling.mode` | Text | `log`, `enforce` | `enforce` | |
| `suspicious_ip_throttling.login_failures_per_ip_per_day` | Ganzzahl | 10-10000 | 100 | |
| `suspicious_ip_throttling.signups_per_ip_per_hour` | Ganzzahl | 1-20 | 20 | höchstens 20 |
| `suspicious_ip_throttling.allowlist` | Textliste | bis zu 100 CIDR-Präfixe; IPv4 nicht breiter als /8, IPv6 nicht breiter als /32 | `[]` | |
| `credential_stuffing.mode` | Text | `log`, `enforce` | `enforce` | |
| `credential_stuffing.attack_mode_auto` | Wahrheitswert | | `true` | |
| `credential_stuffing.attack_mode_duration_minutes` | Ganzzahl | 15-240 | 30 | |
| `bot_protection.mode` | Text | `off`, `risk_based`, `always` | `risk_based` | |
| `bot_protection.provider` | Text | `altcha` | `altcha` | |
| `breached_password.on_signup` | Text | `block` | `block` | `block` |
| `breached_password.on_password_change` | Text | `block` | `block` | `block` |
| `breached_password.on_login` | Text | `require_reset`, `notify` | `require_reset` | |
| `breached_password.notify_user` | Wahrheitswert | | `true` | |
| `signup_protection.block_disposable_email` | Wahrheitswert | | `true` | |
| `signup_protection.block_email_subaddress` | Wahrheitswert | | `false` | |
| `signup_protection.max_signups_per_device_30d` | Ganzzahl | 1-50 | 3 | |
| `sms_protection.allowed_countries` | Textliste | bis zu 250 ISO-3166-1-Alpha-2-Codes | `[]` (Tarifstandard) | |
| `sms_protection.daily_cap` | Ganzzahl \| null | 1-1000000; `null` ist der Tarifwert | `null` | |
| `sms_protection.conversion_breaker` | Wahrheitswert | | `true` | `true` |
| `risk_policy.mode` | Text | `log`, `enforce` (`enforce` erfordert den Tarif Startup oder höher) | `log` | |
| `risk_policy.challenge_threshold` | Ganzzahl | 3-8 | 4 | |
| `risk_policy.block_threshold` | Ganzzahl \| null | 6-12, größer als `challenge_threshold`; `null` ist aus | `null` | |
| `risk_policy.notify_user_on_new_device` | Wahrheitswert | | `true` | |

Ein Wert unterhalb der Sicherheitsuntergrenze wird mit `422 below_security_floor` abgelehnt, auch wenn er im allgemeinen Bereich des Feldes liegt: Die Untergrenze schützt jede Umgebung und kann nicht gesenkt werden.

## Verhalten

- Die Änderung gilt sofort für neue Anmeldungen: Die zwischengespeicherten Einstellungen werden in derselben Transaktion ungültig.
- Der Audit-Eintrag enthält die geänderten Feldnamen sowie das vorherige und das neue Objekt.

## Antwort

`200 OK` mit dem Objekt, wie in [Einstellungen des Angriffsschutzes abrufen](get.md), und dem neuen `ETag`.

## Fehler

| HTTP | Code | Wann |
|---|---|---|
| 400 | `unknown_field` | Ein Abschnitt oder Feld gehört nicht zum Objekt. `errors[]` listet die Pointer. |
| 400 | `malformed_request` | Der Body ist kein gültiges JSON, oder der Header `If-Match` ist fehlerhaft. |
| 401 | `unauthenticated` | Die Anmeldedaten fehlen, sind ungültig oder widerrufen. |
| 403 | `insufficient_permissions` | Den Anmeldedaten fehlt `attack-protection:write`. |
| 403 | `plan_feature_unavailable` | `risk_policy.mode` ist `enforce`, und der Tarif umfasst das nicht; der Body enthält `feature` und `required_plan`. |
| 412 | `precondition_failed` | Das Objekt hat sich seit dem Lesen geändert. Lesen Sie es erneut und versuchen Sie es noch einmal. |
| 413 | `payload_too_large` | Das Objekt ist größer als 32 KiB. |
| 422 | `validation_failed` | Ein Wert hat den falschen Typ oder liegt außerhalb des Bereichs, oder `block_threshold` ist nicht größer als `challenge_threshold`. `errors[]` listet die Pointer. |
| 422 | `below_security_floor` | Ein Wert liegt unterhalb der Sicherheitsuntergrenze. Jeder Eintrag in `errors[]` nennt Feld und Untergrenze in `detail`. |
| 422 | `allowlist_range_too_broad` | Ein Präfix der Allowlist ist breiter als /8 (IPv4) oder /32 (IPv6). |
| 428 | `precondition_required` | `If-Match` fehlt. |
| 429 | `rate_limited` | Das Ratenlimit wurde überschritten; nach der Zeit aus `Retry-After` erneut versuchen. |
| 503 | `service_unavailable` | Eine Abhängigkeit ist vorübergehend nicht verfügbar; nach der Zeit aus `Retry-After` erneut versuchen. |

Treten mehrere Fehlerarten auf, wird nur die schwerwiegendste zurückgegeben, in dieser Reihenfolge: `unknown_field`, `validation_failed`, `below_security_floor`, `allowlist_range_too_broad`, `plan_feature_unavailable`. Die geschriebenen Werte werden in der Antwort nie zurückgegeben.

## Beispiel

```bash
curl -X PUT https://api.eu.keenauth.com/v1/attack-protection \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H 'If-Match: "3f1c9a0b5d7e2a4c6b8d0e1f2a3b4c5d"' \
  -d '{"brute_force": {"max_failures": 10, "lock_minutes": 30}, "bot_protection": {"mode": "always"}}'
```
