
# Remplacer l'ensemble des paramètres de protection contre les attaques

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

Remplace l'objet complet de protection contre les attaques (`application/json`). Les sections et champs omis reviennent à leurs valeurs par défaut. `If-Match` est obligatoire : absent, cela donne `428` ; `ETag` périmé, `412`. Réécrire le même objet n'écrit rien et conserve l'`ETag`. Pour modifier quelques champs, utilisez `PATCH`.

## Requête

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `If-Match` | en-tête | oui | L'`ETag` de l'objet tel que lu en dernier. Absent : `428` ; périmé : `412`. |
| corps | objet | oui | L'objet configuré, au plus 32 KiB. Chaque section est facultative ; chaque champ d'une section l'est aussi. |

| Champ | Type | Plage ou valeurs | Valeur par défaut | Plancher |
|---|---|---|---|---|
| `brute_force.mode` | texte | `log`, `enforce` | `enforce` | |
| `brute_force.max_failures` | entier | 5-100 | 20 | au plus 100 |
| `brute_force.window_minutes` | entier | 5-60 | 15 | |
| `brute_force.lock_minutes` | entier | 5-60 | 15 | au moins 5 |
| `brute_force.notify_user` | booléen | | `true` | |
| `suspicious_ip_throttling.mode` | texte | `log`, `enforce` | `enforce` | |
| `suspicious_ip_throttling.login_failures_per_ip_per_day` | entier | 10-10000 | 100 | |
| `suspicious_ip_throttling.signups_per_ip_per_hour` | entier | 1-20 | 20 | au plus 20 |
| `suspicious_ip_throttling.allowlist` | tableau de texte | jusqu'à 100 préfixes CIDR ; IPv4 pas plus large que /8, IPv6 pas plus large que /32 | `[]` | |
| `credential_stuffing.mode` | texte | `log`, `enforce` | `enforce` | |
| `credential_stuffing.attack_mode_auto` | booléen | | `true` | |
| `credential_stuffing.attack_mode_duration_minutes` | entier | 15-240 | 30 | |
| `bot_protection.mode` | texte | `off`, `risk_based`, `always` | `risk_based` | |
| `bot_protection.provider` | texte | `altcha` | `altcha` | |
| `breached_password.on_signup` | texte | `block` | `block` | `block` |
| `breached_password.on_password_change` | texte | `block` | `block` | `block` |
| `breached_password.on_login` | texte | `require_reset`, `notify` | `require_reset` | |
| `breached_password.notify_user` | booléen | | `true` | |
| `signup_protection.block_disposable_email` | booléen | | `true` | |
| `signup_protection.block_email_subaddress` | booléen | | `false` | |
| `signup_protection.max_signups_per_device_30d` | entier | 1-50 | 3 | |
| `sms_protection.allowed_countries` | tableau de texte | jusqu'à 250 codes ISO 3166-1 alpha-2 | `[]` (valeur du plan) | |
| `sms_protection.daily_cap` | entier \| null | 1-1000000 ; `null` est la valeur du plan | `null` | |
| `sms_protection.conversion_breaker` | booléen | | `true` | `true` |
| `risk_policy.mode` | texte | `log`, `enforce` (`enforce` requiert le plan Startup ou supérieur) | `log` | |
| `risk_policy.challenge_threshold` | entier | 3-8 | 4 | |
| `risk_policy.block_threshold` | entier \| null | 6-12, supérieur à `challenge_threshold` ; `null` désactive | `null` | |
| `risk_policy.notify_user_on_new_device` | booléen | | `true` | |

Une valeur en dessous du plancher de sécurité est refusée avec `422 below_security_floor`, même si elle se situe dans la plage générale du champ : le plancher protège tous les environnements et ne peut pas être abaissé.

## Comportement

- La modification s'applique immédiatement aux nouvelles connexions : les paramètres en cache sont invalidés dans la même transaction.
- L'enregistrement d'audit contient les noms des champs modifiés, ainsi que l'objet précédent et le nouveau.

## Réponse

`200 OK` avec l'objet, comme dans [Obtenir les paramètres de protection contre les attaques](get.md), et le nouvel `ETag`.

## Erreurs

| HTTP | code | Quand |
|---|---|---|
| 400 | `unknown_field` | Une section ou un champ ne fait pas partie de l'objet. `errors[]` liste les pointeurs. |
| 400 | `malformed_request` | Le corps n'est pas un JSON valide, ou l'en-tête `If-Match` est mal formé. |
| 401 | `unauthenticated` | L'identifiant est absent, invalide ou révoqué. |
| 403 | `insufficient_permissions` | L'identifiant n'a pas `attack-protection:write`. |
| 403 | `plan_feature_unavailable` | `risk_policy.mode` vaut `enforce` et le plan ne l'inclut pas ; le corps contient `feature` et `required_plan`. |
| 412 | `precondition_failed` | L'objet a changé depuis votre lecture. Relisez-le et réessayez. |
| 413 | `payload_too_large` | L'objet dépasse 32 KiB. |
| 422 | `validation_failed` | Une valeur a le mauvais type ou est hors plage, ou `block_threshold` n'est pas supérieur à `challenge_threshold`. `errors[]` liste les pointeurs. |
| 422 | `below_security_floor` | Une valeur est sous le plancher de sécurité. Chaque entrée de `errors[]` nomme le champ et son plancher dans `detail`. |
| 422 | `allowlist_range_too_broad` | Un préfixe de liste d'autorisation est plus large que /8 (IPv4) ou /32 (IPv6). |
| 428 | `precondition_required` | `If-Match` est absent. |
| 429 | `rate_limited` | La limite de débit est dépassée ; réessayez après la durée `Retry-After`. |
| 503 | `service_unavailable` | Une dépendance est temporairement indisponible ; réessayez après la durée `Retry-After`. |

Si plusieurs types d'erreur sont présents, seul le plus grave est renvoyé, dans cet ordre : `unknown_field`, `validation_failed`, `below_security_floor`, `allowlist_range_too_broad`, `plan_feature_unavailable`. Les valeurs écrites ne sont jamais renvoyées dans la réponse.

## Exemple

```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"}}'
```
