
# Sostituisci le impostazioni della protezione dagli attacchi

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

Sostituisce l'intero oggetto di protezione dagli attacchi (`application/json`). Le sezioni e i campi omessi tornano ai valori predefiniti. `If-Match` è obbligatorio: se manca si ottiene `428`, se l'`ETag` è obsoleto `412`. Riscrivere lo stesso oggetto non scrive nulla e mantiene l'`ETag`. Per modificare pochi campi, usate `PATCH`.

## Richiesta

| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
| `If-Match` | header | sì | L'`ETag` dell'oggetto così come letto l'ultima volta. Se manca: `428`; se obsoleto: `412`. |
| corpo | oggetto | sì | L'oggetto configurato, al massimo 32 KiB. Ogni sezione è facoltativa; anche ogni campo al suo interno è facoltativo. |

| Campo | Tipo | Intervallo o valori | Predefinito | Minimo |
|---|---|---|---|---|
| `brute_force.mode` | testo | `log`, `enforce` | `enforce` | |
| `brute_force.max_failures` | intero | 5-100 | 20 | al massimo 100 |
| `brute_force.window_minutes` | intero | 5-60 | 15 | |
| `brute_force.lock_minutes` | intero | 5-60 | 15 | almeno 5 |
| `brute_force.notify_user` | booleano | | `true` | |
| `suspicious_ip_throttling.mode` | testo | `log`, `enforce` | `enforce` | |
| `suspicious_ip_throttling.login_failures_per_ip_per_day` | intero | 10-10000 | 100 | |
| `suspicious_ip_throttling.signups_per_ip_per_hour` | intero | 1-20 | 20 | al massimo 20 |
| `suspicious_ip_throttling.allowlist` | array di testo | fino a 100 prefissi CIDR; IPv4 non più ampio di /8, IPv6 non più ampio di /32 | `[]` | |
| `credential_stuffing.mode` | testo | `log`, `enforce` | `enforce` | |
| `credential_stuffing.attack_mode_auto` | booleano | | `true` | |
| `credential_stuffing.attack_mode_duration_minutes` | intero | 15-240 | 30 | |
| `bot_protection.mode` | testo | `off`, `risk_based`, `always` | `risk_based` | |
| `bot_protection.provider` | testo | `altcha` | `altcha` | |
| `breached_password.on_signup` | testo | `block` | `block` | `block` |
| `breached_password.on_password_change` | testo | `block` | `block` | `block` |
| `breached_password.on_login` | testo | `require_reset`, `notify` | `require_reset` | |
| `breached_password.notify_user` | booleano | | `true` | |
| `signup_protection.block_disposable_email` | booleano | | `true` | |
| `signup_protection.block_email_subaddress` | booleano | | `false` | |
| `signup_protection.max_signups_per_device_30d` | intero | 1-50 | 3 | |
| `sms_protection.allowed_countries` | array di testo | fino a 250 codici ISO 3166-1 alpha-2 | `[]` (predefinito del piano) | |
| `sms_protection.daily_cap` | intero \| null | 1-1000000; `null` è il valore del piano | `null` | |
| `sms_protection.conversion_breaker` | booleano | | `true` | `true` |
| `risk_policy.mode` | testo | `log`, `enforce` (`enforce` richiede il piano Startup o superiore) | `log` | |
| `risk_policy.challenge_threshold` | intero | 3-8 | 4 | |
| `risk_policy.block_threshold` | intero \| null | 6-12, maggiore di `challenge_threshold`; `null` è disattivato | `null` | |
| `risk_policy.notify_user_on_new_device` | booleano | | `true` | |

Un valore sotto la soglia minima di sicurezza viene rifiutato con `422 below_security_floor`, anche se rientra nell'intervallo generale del campo: la soglia protegge ogni ambiente e non può essere abbassata.

## Comportamento

- La modifica si applica subito ai nuovi accessi: le impostazioni in cache vengono invalidate nella stessa transazione.
- Il record di audit contiene i nomi dei campi modificati e l'oggetto precedente e quello nuovo.

## Risposta

`200 OK` con l'oggetto, come in [Ottieni le impostazioni della protezione dagli attacchi](get.md), e il nuovo `ETag`.

## Errori

| HTTP | codice | Quando |
|---|---|---|
| 400 | `unknown_field` | Una sezione o un campo non fa parte dell'oggetto. `errors[]` elenca i puntatori. |
| 400 | `malformed_request` | Il corpo non è un JSON valido, oppure l'header `If-Match` è malformato. |
| 401 | `unauthenticated` | La credenziale è mancante, non valida o revocata. |
| 403 | `insufficient_permissions` | La credenziale non ha `attack-protection:write`. |
| 403 | `plan_feature_unavailable` | `risk_policy.mode` è `enforce` ma il piano non lo include; il corpo contiene `feature` e `required_plan`. |
| 412 | `precondition_failed` | L'oggetto è cambiato dopo la lettura. Leggetelo di nuovo e riprovate. |
| 413 | `payload_too_large` | L'oggetto supera 32 KiB. |
| 422 | `validation_failed` | Un valore ha il tipo sbagliato o è fuori intervallo, oppure `block_threshold` non è maggiore di `challenge_threshold`. `errors[]` elenca i puntatori. |
| 422 | `below_security_floor` | Un valore è sotto la soglia minima di sicurezza. Ogni voce di `errors[]` indica campo e soglia in `detail`. |
| 422 | `allowlist_range_too_broad` | Un prefisso della lista di autorizzazione è più ampio di /8 (IPv4) o /32 (IPv6). |
| 428 | `precondition_required` | `If-Match` manca. |
| 429 | `rate_limited` | È stato superato il limite di frequenza; riprovate dopo il periodo indicato in `Retry-After`. |
| 503 | `service_unavailable` | Una dipendenza non è disponibile temporaneamente; riprovate dopo il periodo indicato in `Retry-After`. |

Se sono presenti più tipi di errore, viene restituito solo il più grave, in quest'ordine: `unknown_field`, `validation_failed`, `below_security_floor`, `allowlist_range_too_broad`, `plan_feature_unavailable`. I valori scritti non vengono mai restituiti nella risposta.

## Esempio

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