Un point de terminaison qui accepte une requête POST et déclenche une écriture en base de données : formulé ainsi, ce mécanisme sonnerait immédiatement l’alarme dans une revue de sécurité. C’est pourtant exactement la définition d’un webhook entrant, et c’est exactement ce que reçoit WordPress lorsqu’un outil d’emailing comme Sendinblue notifie un événement (désabonnement, ouverture, clic, bounce) vers une route personnalisée du site.
Sur de nombreuses intégrations observées, ce point de terminaison ne vérifie strictement rien : il fait confiance à la structure JSON reçue, met à jour le statut d’abonnement d’un contact en base, et répond 200 sans jamais se demander si la requête provient réellement de Sendinblue ou de n’importe quel client HTTP ayant deviné l’URL.
Pourquoi un webhook non vérifié est une porte ouverte
Une route WordPress qui reçoit un webhook est, du point de vue de la sécurité, équivalente à un formulaire public : n’importe qui connaissant son URL peut lui envoyer une requête. La seule différence est que ce formulaire ne s’affiche jamais à l’écran, ce qui donne l’illusion trompeuse d’une certaine confidentialité. Or l’URL d’un webhook, une fois configurée dans le tableau de bord Sendinblue, finit souvent par apparaître dans des journaux de configuration, des captures d’écran de documentation interne ou des dépôts de code, sans qu’on y pense comme à un secret à protéger.
Si cette route déclenche une écriture sans vérification, n’importe qui peut désabonner arbitrairement des contacts, forger de faux événements d’ouverture pour fausser des statistiques, ou dans le pire des cas exploiter une faille de désérialisation si le contenu du webhook est traité sans validation stricte de son format.
La checklist en deux vérifications
- Vérifier la signature du webhook : Sendinblue transmet un en-tête permettant de valider l’authenticité de la requête via une clé secrète partagée, à comparer avec un HMAC calculé côté serveur sur le corps brut de la requête, jamais sur le JSON déjà décodé.
- Contrôler l’idempotence de l’événement : chaque notification porte un identifiant d’événement unique, à comparer à un registre des événements déjà traités pour éviter qu’un rejeu de la même requête (accidentel ou malveillant) ne déclenche deux fois la même action.

Implémenter la vérification de signature
La vérification doit s’effectuer sur le corps brut de la requête HTTP, avant tout décodage JSON, car un octet de différence dans l’encodage change entièrement le résultat du calcul HMAC :
add_action('rest_api_init', function () {
register_rest_route('webhooks/v1', '/sendinblue', [
'methods' => 'POST',
'callback' => 'traiter_webhook_sendinblue',
'permission_callback' => 'verifier_signature_sendinblue',
]);
});
function verifier_signature_sendinblue(WP_REST_Request $request) {
$corps_brut = $request->get_body();
$signature_recue = $request->get_header('x-mailin-signature');
$signature_calculee = hash_hmac('sha256', $corps_brut, getenv('SENDINBLUE_WEBHOOK_SECRET'));
return hash_equals($signature_calculee, $signature_recue);
}
L’usage de hash_equals() plutôt qu’une comparaison directe avec === n’est pas cosmétique : cette fonction protège contre les attaques par mesure de temps, qui pourraient permettre de deviner progressivement une signature valide en observant les micro-variations de temps de réponse.
Gérer le rejeu et les doublons
Une fois la signature vérifiée, l’identifiant d’événement transmis par Sendinblue doit être enregistré dans une table dédiée avant traitement, avec un contrôle d’unicité qui empêche silencieusement tout traitement en double. Cette précaution protège contre le rejeu volontaire d’une requête interceptée, mais aussi contre les doublons légitimes que certains services d’emailing envoient parfois en cas de latence réseau de leur côté.
En résumé
Un webhook entrant mérite exactement le même niveau de méfiance qu’un formulaire public, avec une difficulté supplémentaire : son URL n’est jamais affichée à l’écran, ce qui pousse trop souvent à négliger sa protection. Vérifier la signature sur le corps brut de la requête et contrôler l’idempotence de chaque événement transforment ce point d’entrée en canal fiable, sans complexité disproportionnée par rapport au risque qu’il représente.