# Protection anti-spam

> Honeypot, horodatages signés, score de spam, vérification humaine, domaines autorisés et limites de débit.

Chaque formulaire bénéficie d'une protection en couches sans configuration : un honeypot, un horodatage d'affichage signé, un score de spam et des limites de débit. Vous pouvez y ajouter une vérification humaine et limiter le formulaire à vos propres domaines.

| Couche | Active par défaut | Résultat |
| --- | --- | --- |
| [Limites de débit](https://ikiform.com/fr/docs/rate-limits.md) | Oui, préréglage Standard | `429 rate_limited` |
| [Domaines autorisés](#allowed-domains) | Non | Spam, ou `403 origin_not_allowed` |
| [Vérification humaine](#human-check) | Non | `403 challenge_required` ou une page de vérification |
| [Honeypot](#honeypot) | Oui | +5 au score |
| [Score de spam](#scoring) | Oui | Spam à partir de 5 |

## Comment le spam est traité

Un envoi jugé comme spam est stocké avec le statut `spam` et reçoit quand même une réponse de succès normale : les bots n'apprennent rien. Le spam n'envoie ni webhook ni e-mail, ne compte pas dans votre usage mensuel et est supprimé au bout de 30 jours. Examinez-le dans le tableau de bord ou avec l'[API REST](https://ikiform.com/fr/docs/api.md#mark-spam) ; marquer une soumission comme légitime déclenche alors ses webhooks.

## Honeypot

Ajoutez un input masqué que les humains ne remplissent jamais. Le nom par défaut est `_gotcha` ; vous pouvez le changer par formulaire sous **Honeypot field** dans les paramètres du formulaire. Les techniques pour le masquer sont sur [Formulaires HTML](https://ikiform.com/fr/docs/html.md#honeypot).

`Honeypot`:

```html
<input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
```

## Score de spam

Chaque envoi reçoit un score calculé à partir de ces signaux. À partir de 5, il est stocké comme spam.

| Signal | Raison dans le tableau de bord | Score |
| --- | --- | --- |
| Le honeypot est rempli | `honeypot` | +5 |
| Envoyé moins de 2 secondes après l'affichage du formulaire | `timestamp_fast` | +3 |
| L'horodatage d'affichage signé est falsifié ou date de plus de 24 heures | `timestamp_invalid` | +3 |
| Un lien dans un champ de nom ou d'objet | `url_in_name` | +3 |
| Les mêmes champs ont été envoyés à ce formulaire dans les dernières 24 heures | `duplicate` | +3 |
| Trois liens ou plus au total | `many_urls` | +2 |
| Une phrase de spam connue (22 intégrées) | `keyword` | +2 |
| Une adresse e-mail sur un domaine d'e-mail jetable | `disposable_email` | +2 |

- L'horodatage d'affichage n'existe que si le formulaire est affiché par le SDK ou le [composant shadcn/ui](https://ikiform.com/fr/docs/shadcn.md), qui envoient un `_ikiform_ts` signé. Les envois HTML simples qui ne l'ont pas ne sont pas pénalisés.
- Un champ de nom est tout champ dont le nom contient `name`, `firstname`, `lastname`, `fullname`, `surname` ou `subject`, mais pas des mots comme `company`, `website` ou `domain`.
- Deux raisons imposent le spam quel que soit le score : `origin_mismatch`, issue des [domaines autorisés](#allowed-domains), et `captcha_unavailable` quand Turnstile est injoignable.

## Vérification humaine

Chaque formulaire a un réglage **Spam check** avec trois options. Il est désactivé par défaut, pour que les intégrations `fetch` et curl existantes continuent de fonctionner quand vous créez un formulaire.

| Option | Rôle |
| --- | --- |
| Off | Aucune vérification. Le honeypot, l'horodatage et le score restent actifs. |
| Ikiform (recommandé) | Cloudflare Turnstile exécuté par Ikiform sur ikiform.com. Aucun compte ni clé Cloudflare nécessaire. |
| Vos propres clés Turnstile | Votre widget Turnstile et votre clé secrète. Le nom d'hôte du token est comparé à vos domaines autorisés. |

### Comment fonctionne la vérification Ikiform

La façon d'obtenir le token dépend de votre mode d'envoi :

- **Envoi de formulaire HTML :** l'envoi sans token reçoit une courte page « Checking you're human » sur ikiform.com. Elle exécute Turnstile et renvoie les champs d'origine avec le token, puis le navigateur suit votre redirection comme d'habitude.
- **JavaScript :** appelez `challenge(formId)` du SDK. Il monte une petite frame ikiform.com qui reste invisible sauf si Turnstile a besoin d'un clic, et se résout avec un token. Passez-le à `submit` dans `turnstileToken`. Un envoi `fetch` sans token reçoit `403 challenge_required`.
- **Composant shadcn/ui :** géré pour vous.
- **Serveur avec une clé secrète :** la vérification est ignorée, puisqu'un serveur ne peut pas la résoudre.

`form.js`:

```js
import { challenge, submit } from "@ikiform/sdk";

const form = document.querySelector("form");

form.addEventListener("submit", async (event) => {
  event.preventDefault();
  const turnstileToken = await challenge("frm_xxxxxxxxxxxxxxxx");
  const result = await submit("frm_xxxxxxxxxxxxxxxx", new FormData(form), { turnstileToken });
  if (result.ok) form.replaceWith("Thanks, we got it.");
});
```

Les tokens sont à usage unique et valables 300 secondes : obtenez-en un par envoi. Un envoi JSON sans token reçoit `403 challenge_required` avec une `challenge_url` ; un token dont la vérification échoue reçoit `403 captcha_failed`.

`403 Forbidden`:

```json
{
  "ok": false,
  "error": {
    "code": "challenge_required",
    "message": "Complete the human check, then send the form again.",
    "challenge_url": "https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx/challenge"
  },
  "request_id": "req_01k7v2c9t3e8m5q4x6z0a1b2c3"
}
```

> Si Turnstile lui-même est injoignable, l'envoi est accepté et stocké comme spam avec la raison `captcha_unavailable`, pour que vous puissiez le restaurer au lieu de le perdre.

### Vos propres clés Turnstile

Collez votre clé de site et votre clé secrète dans les paramètres du formulaire. Le secret est chiffré au repos. Affichez le widget dans votre formulaire pour qu'il ajoute `cf-turnstile-response` à l'envoi, ou passez le token à `submit` dans `turnstileToken`. Les envois sans token valide reçoivent `403 captcha_failed`, y compris ceux avec une clé API secrète.

## Domaines autorisés

Listez les domaines où se trouve votre formulaire sous **Allowed domains**. Un envoi doit alors venir de l'un d'eux ou d'un sous-domaine, d'après l'en-tête `Origin`, ou le `Referer` en l'absence d'origine. Laissez la liste vide pour accepter les envois de n'importe quel site.

| Domaines autorisés | Envoi depuis | Correspond |
| --- | --- | --- |
| `example.com` | `https://example.com` | Oui |
| `example.com` | `https://www.example.com` | Oui, sous-domaine |
| `example.com` | `http://localhost:3000` | Non, ajoutez `localhost` pendant le développement |
| `example.com` | `https://example.co` | Non |
| `app.example.com` | `https://example.com` | Non, un domaine parent ne correspond pas |

**When a post comes from another site** détermine ce qui arrive en cas de non-correspondance : **Mark as spam** (par défaut) ou **Reject** avec `403 origin_not_allowed`. Les envois avec une clé API secrète contournent ce contrôle. Les domaines autorisés déterminent aussi quelles cibles `_redirect` sont suivies et où la frame de vérification Ikiform peut être intégrée.

## Limites de débit

Chaque formulaire a un préréglage **Rate limit**, compté exactement par IP de visiteur et par formulaire. Standard autorise 5 envois par minute, Relaxed 20 par minute et Strict 2 toutes les 10 minutes. Les règles complètes sont sur [Limites de débit](https://ikiform.com/fr/docs/rate-limits.md).
