Symptômes · 10 min · publié le 9 juin 2024

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.

Réponse directe

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.

formulaire contact spam contact form piraté site envoie spam

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.

Si l'hébergeur a déjà parlé d'abuse mail, lisez le motif. Un quota `mail()` dépassé oriente vers PHP local, pas vers Gmail de l'attaquant.

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.

Les antivirus de messagerie (votre Outlook) ne voient pas la file serveur. Un inbox propre chez vous et un abuse chez OVH, c'est cohérent.

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.

Questions fréquentes

Le formulaire a un captcha, donc il est innocent ?

+
Pas forcément. Un captcha n'empêche pas un destinataire réécrit, ni un `send.php` à côté. Il réduit les bots sur le front. C'est tout.

Dois-je changer d'adresse d'expéditeur ?

+
Après nettoyage, une adresse neuve (`contact2@`) peut aider si l'ancienne est brûlée. Ça ne remplace pas la coupure du script. Prévenez vos clients de la nouvelle adresse, sinon ils traitent le légitime comme du spam.

OVH a coupé le mail() . Est-ce réglé ?

+
L'envoi PHP est coupé, le SMTP du CMS peut encore partir, et le script peut attendre la réouverture. Traitez la cause. Demandez les logs du pic.

Un client a cliqué un lien dans le faux e-mail. Que faire ?

+
Si le lien va vers votre domaine, coupez cette URL. Si c'est un tiers, vous ne la contrôlez pas : dites-lui de changer son mot de passe de messagerie et de signaler. Calibrez le reste avec le constat.

Combien de temps avant que Gmail nous accepte à nouveau ?

+
Variable : jours à semaines selon le volume. Les logs doivent être propres d'abord. Relancer un envoi massif « pour tester » rallonge. Ce n'est pas l'horloge Safe Browsing (24 à 72 h).
À lire ensuite
Mon site envoie du spam Compte SMTP du CMS PHP mail() abuse Journal d'envoi à demander Déclarer mon site