# Un formulaire Sendinblue mal protégé transformé en relais de spam

> Un formulaire d'inscription à une newsletter a été détourné pour envoyer massivement du spam via l'API d'un prestataire d'emailing. Diagnostic et correctifs de validation d'origine et d'anti-bot.

- Auteur : WordPress Développement
- Publié le : 2022-06-12
- Mis à jour le : 2022-06-12
- Catégorie : Sécurité
- URL : https://www.wpmoderne.fr/securite/formulaire-sendinblue-relais-spam/

## L’essentiel

- Un formulaire d'inscription peut être détourné sans jamais toucher au serveur du site
- Vérifier l'origine de la requête élimine l'essentiel des abus automatisés
- Un anti-bot comportemental complète utilement un simple contrôle d'origine

Comment un formulaire d'inscription à une newsletter, aussi simple qu'un champ e-mail et un bouton « S'abonner », peut-il servir à envoyer douze mille e-mails de spam en une seule nuit sans qu'aucun serveur WordPress ne soit directement compromis ? La réponse tenait dans la façon dont ce formulaire communiquait avec l'API Sendinblue utilisée pour gérer les inscriptions.

Le formulaire envoyait, via une requête JavaScript côté navigateur, directement à l'API publique de Sendinblue, une clé API restreinte à la création de contacts, accompagnée d'un champ personnalisé optionnel destiné à personnaliser l'e-mail de bienvenue envoyé automatiquement à chaque nouvel inscrit. C'est précisément ce champ personnalisé qui a été détourné.

## Symptôme : des inscriptions en masse depuis des origines suspectes

Le tableau de bord Sendinblue a signalé un pic d'inscriptions anormal, plusieurs milliers en quelques heures, très majoritairement issues d'adresses e-mail visiblement générées automatiquement. Un examen plus poussé a révélé que le champ personnalisé « message de bienvenue », censé permettre à l'inscrit de personnaliser une phrase d'accueil, était en réalité rempli avec des liens promotionnels vers des sites tiers, injectés directement via des appels API automatisés ne passant jamais par le formulaire visible du site.

Le service, configuré pour renvoyer automatiquement un e-mail de confirmation incluant ce champ personnalisé, servait donc de relais d'envoi massif : chaque inscription frauduleuse générait un e-mail légitime, envoyé depuis l'infrastructure de confiance de Sendinblue, contenant le contenu promotionnel inséré par l'attaquant, avec une délivrabilité bien meilleure que s'il avait tenté d'envoyer ce spam directement depuis ses propres serveurs.

## Diagnostic : une API publique appelée sans vérification d'origine

La clé API utilisée, bien que restreinte à la création de contacts, restait exploitable depuis n'importe quelle origine, aucune vérification ne limitant les appels au domaine du site officiel. Un script pouvait donc appeler directement l'endpoint Sendinblue avec cette même clé, extraite du code source public de la page, sans jamais transiter par le formulaire visible ni déclencher les protections front-end habituelles (validation JavaScript, honeypot éventuel), toutes contournées puisque la requête ne passait jamais par la page elle-même.

> L'essentiel à retenir : Un formulaire d'inscription peut être détourné sans jamais toucher au serveur du site ; Vérifier l'origine de la requête élimine l'essentiel des abus automatisés ; Un anti-bot comportemental complète utilement un simple contrôle d'origine

## Correctif : passer par un proxy WordPress avec vérification d'origine

La correction a supprimé l'appel direct au navigateur vers l'API Sendinblue, remplacé par une route REST WordPress qui reçoit la demande d'inscription, vérifie plusieurs signaux avant de relayer l'appel, et seule détient désormais la clé API :

```
function inscrire_newsletter(WP_REST_Request $request) {
    $referer = wp_get_referer();
    if (!$referer || parse_url($referer, PHP_URL_HOST) !== parse_url(home_url(), PHP_URL_HOST)) {
        return new WP_Error('origine_invalide', 'Requête refusée.', ['status' => 403]);
    }

    if (!wp_verify_nonce($request->get_param('_wpnonce'), 'inscription_newsletter')) {
        return new WP_Error('nonce_invalide', 'Session expirée.', ['status' => 403]);
    }

    $email = sanitize_email($request->get_param('email'));
    if (!is_email($email)) {
        return new WP_Error('email_invalide', 'Adresse invalide.', ['status' => 400]);
    }

    $message_perso = wp_strip_all_tags($request->get_param('message'));
    if (preg_match('#https?://#i', $message_perso)) {
        $message_perso = '';
    }

    $reponse = wp_remote_post('https://api.sendinblue.com/v3/contacts', [
        'headers' => ['api-key' => getenv('SENDINBLUE_API_KEY'), 'content-type' => 'application/json'],
        'body'    => wp_json_encode(['email' => $email, 'attributes' => ['MESSAGE' => $message_perso]]),
    ]);

    return ['statut' => 'inscrit'];
}
```

Le rejet de tout lien détecté dans le champ personnalisé constitue une règle simple mais efficace : un message de bienvenue légitime n'a aucune raison de contenir une URL.

## Un anti-bot comportemental en complément

La vérification de nonce et d'origine élimine les appels directs à l'API, mais reste contournable par un script capable de charger la page complète du formulaire avant de soumettre. Un délai minimal de soumission et un compteur d'inscriptions par adresse IP sur une fenêtre glissante ont été ajoutés en complément, réduisant encore le volume résiduel de tentatives automatisées.

## En résumé

Une clé API accessible côté navigateur, même restreinte dans ses permissions, reste utilisable par n'importe quel script qui la récupère dans le code source, indépendamment de l'interface prévue pour son usage. Faire transiter chaque appel par une route serveur qui vérifie l'origine, le nonce et le contenu soumis referme cette voie de détournement sans remettre en cause l'intégration elle-même.
