Le formulaire de contact envoie du spam à votre insu
Vos clients reçoivent des e-mails « de votre part » que vous n'avez pas tapés, ou votre boîte croule sous les copies cachées. Le formulaire est détourné, ou un script à côté utilise `mail()` du serveur. On coupe l'envoi, on lit les journaux, on change le SMTP du CMS — avant de « réchauffer » le domaine.
Le formulaire est détourné, ou un script parallèle utilise la fonction mail() du serveur. Coupez l'envoi, lisez les journaux, changez le SMTP du CMS.
Deux sources, un même symptôme
Le destinataire voit `@votre-domaine.fr` ou le nom de votre site. Ça ne dit pas qui a envoyé. Soit quelqu'un abuse le formulaire (injection dans les en-têtes, relais ouvert, destinataire réécrit). Soit un PHP ailleurs appelle `mail()` ou le SMTP que le CMS a en mémoire. Traiter seulement le plugin de contact laisse le second tourner.
Un troisième cas existe : l'enveloppe est usurpée (SPF permissif) depuis une machine qui n'est pas la vôtre. Vos journaux d'hébergement sont alors vides. Commencez quand même par le serveur : c'est le cas le plus fréquent quand « ça part vraiment de chez nous ».
Distinguez aussi le spam que vous recevez (le formulaire marche trop bien, les bots vous écrivent) du spam que les autres reçoivent en votre nom. Le premier est de la nuisance. Le second brûle le domaine et l'IP.
Couper l'envoi sans jeter le formulaire
Désactivez l'envoi : mode maintenance du plugin, SMTP vers une adresse nulle, ou `php.ini` `mail()` coupé par l'hébergeur le temps de l'incident. Le formulaire peut rester à l'écran avec un message « réécriture temporaire ». Jeter le shortcode casse la page Contact et n'arrête pas un script à la racine.
Notez l'heure de la coupure. Les plaintes qui arrivent après cette heure ne viennent plus de ce canal — ou alors la coupure est incomplète. C'est le test le plus simple du dossier.
Prévenez l'hébergeur que vous intervenez. Un ticket « nous coupons l'envoi, merci de conserver les logs mail » évite une suspension automatique au moment où vous avez besoin du FTP.
- Plugin contact : envoi off.
- SMTP du CMS : mot de passe révoqué.
- Demander le journal d'envoi à l'hébergeur.
- Formulaire visible, file d'attente coupée.
Le formulaire détourné : champs cachés et destinataire
Ouvrez la config du formulaire. Le destinataire est-il encore le vôtre ? Un champ BCC ajouté, un `To:` passé en dynamique depuis un input, un webhook vers une URL inconnue : trois classiques. Contact Form 7 stocke ça dans un `wp_posts` de type `wpcf7_contact_form` : lisez le post, pas seulement l'UI.
Les en-têtes : si le plugin construit `Reply-To` ou `From` avec un champ libre, un bot injecte des destinataires. Le correctif n'est pas « plus de formulaire » ; c'est un `From` fixe (une adresse du domaine) et un `Reply-To` filtré. Les versions récentes le font ; les copies de 2018 non.
Un second formulaire invisible (shortcode dans le footer, page en brouillon servie au bot) suffit. Cherchez le shortcode dans `wp_posts` et les widgets. Voir aussi liens et HTML injectés : le même passage ajoute parfois un form.
Le script parallèle : `mail()` et cron
Un `send.php` à la racine, un cron qui lance `php /home/…/tmp/x.php`, un mu-plugin de dix lignes : le formulaire officiel est innocent. Les journaux d'envoi (Exim, Postfix, « tracked email » o2switch) montrent le script appelant, pas le nom du plugin.
Cherchez `mail(` et `wp_mail(` dans les fichiers modifiés récemment, pas dans tout WordPress (trop de faux positifs). Les dates de modification raccourcissent. Un fichier `wp-comments-post.php` plus lourd que l'officiel est un suspect classique.
Les tâches cron du panel valent plus que WP-Cron pour l'envoi de nuit. Listez-les avant de « optimiser » quoi que ce soit.
Lire les journaux, pas seulement la boîte
La boîte de l'agence reçoit une copie sur vingt. Le journal serveur liste tout : destinataire, heure, authentifié ou `mail()`. Demandez-le. Sans lui, vous déboguez à l'aveugle et vous réchauffez un domaine encore sale.
Un pic à heure fixe (toutes les heures + 2 min) est un cron. Un pic à chaque hit `/contact` est le formulaire. Un pic sans hit web est un processus CLI. Trois motifs, trois endroits.
Gardez une extrait de log (anonymisez les destinataires si vous le transmettez). Ça sert au constat et à l'hébergeur qui veut une preuve que la cause est traitée, pas un « c'est bon ». Voir journal d'envoi.
Changer le SMTP du CMS
WP Mail SMTP, Fluent, un `define` dans `wp-config`, Brevo, Mailgun : révoquez la clé, créez-en une nouvelle, tournez le mot de passe de la boîte. L'ancienne clé dans un `wp_options` ou un `.env` a pu être lue. Laisser la même, c'est offrir le relais.
Si le CMS n'avait pas de SMTP et passait par `mail()` PHP, c'est le moment d'en poser un — après nettoyage, pas pendant. Pendant l'incident, moins de canaux, pas plus.
Search Console et Safe Browsing n'ont rien à voir avec ce spam-là, sauf si les e-mails portent un lien vers une page d'hameçonnage sur votre domaine. Dans ce cas, traitez aussi cette URL.
Contact Form 7, WPForms, Presta : pièges fréquents
Contact Form 7 : Flamingo ou une table de messages peut montrer le vrai contenu envoyé. Une extension « CF7 to… » tierce est un second canal. WPForms et Gravity : regarder les notifications (plusieurs e-mails, BCC). Un notification ajoutée « Admin 2 » avec un Gmail inconnu est le BCC pirate.
PrestaShop : le formulaire contact écrit parfois en base (`ps_customer_thread`) et envoie. Un override du `ContactController` est plus discret qu'un module. Magento : les transactional e-mails ont des templates ; un template réécrit avec un destinataire en plus se voit dans le HTML.
Un captcha cassé (clé reCAPTCHA expirée, widget invisible qui ne s'affiche plus) transforme un bon formulaire en relais. Testez-le comme un inconnu, pas connecté.
Après la coupure : délivrabilité
Ne « réchauffez » pas le domaine tant que les logs montrent encore des envois. SPF, DKIM, DMARC se corrigent après, pas à la place du script. Un SPF trop large (`+all`) est un autre sujet : il permet l'usurpation hors serveur.
Les listes (Spamhaus, etc.) se vident en jours, pas en heures. Relancer un mailing « on est désolé » le soir même aggrave. Attendez le calme des logs, puis un e-mail de test vers Gmail et Orange.
Si Chrome bloque le site, c'est une autre file. Le spam mail ne lève pas Safe Browsing, et inversement. Voir retrait blacklist Google seulement s'il y a une alerte navigateur.
Quand rouvrir le formulaire
Quand les journaux sont calmes 24 h, que le destinataire est le vôtre, que le `From` est fixe, qu'un captcha tient, et qu'aucun cron n'appelle `mail()`. Un test vous-même vers une boîte perso. Puis surveillez 48 h.
Si les envois reprennent pile à la réouverture du plugin, le problème était le formulaire. S'ils reprennent sans hit `/contact`, le script parallèle est encore là. Ne rouvrez pas en boucle pour « voir ».
Vous pouvez déclarer le site si les logs et le PHP se mélangent. En attendant : coupure envoi, révocation SMTP, ticket hébergeur. Trois gestes qui tiennent une heure.
- 24 h de logs calmes.
- From fixe, captcha testé.
- Pas de cron mail inconnu.
- Test Gmail + Orange avant tout mailing.