▶ Structure technique dédiée — Expertise web & mobile — Basée en France
Intervention · E-mail · Sécurité

Un formulaire spammé révèle un problème d’identité e-mail

Des messages de non-remise, des e-mails qui semblent venir d’un autre domaine : le problème ressemblait à un serveur mail compromis. En suivant le message dans les journaux, nous avons trouvé deux causes distinctes, et corrigé les deux.

Contexte

Une application Symfony, hébergée sur un serveur qui en héberge plusieurs, propose un formulaire de contact public. Il est protégé par le jeton CSRF de Symfony, et les données saisies sont validées avant l’envoi. Sur le papier, tout est en ordre.

Pourtant, des messages de non-remise apparaissent : le fournisseur SMTP refuse des e-mails dont le domaine d’expédition ne correspond pas au compte utilisé pour l’envoi. Et ces e-mails contiennent des données saisies dans le formulaire.

L’objectif : comprendre précisément d’où viennent ces e-mails, puis corriger l’envoi et empêcher les soumissions automatiques du formulaire.

  • Le serveur ou le compte d’envoi a été compromis
  • Une application légitime du serveur envoie avec une mauvaise identité

Avant de toucher à la configuration, il fallait savoir laquelle était la bonne.

Le diagnostic : suivre le message avant de corriger

Le message de rejet contient l’identifiant du message dans le serveur mail. Les journaux montrent alors son parcours exact : il n’est pas arrivé d’Internet, il a été produit sur le serveur lui-même. Pas de compromission, mais une application légitime.

Parcours du message : du robot au rejet par le fournisseur SMTP Robot Application Serveur mail Fournisseur SMTP 1. Charge le formulaire de contact 2. Le soumet une seconde plus tard 3. Envoie avec l’adresse de son domaine 4. Remplace l’expéditeur 5. S’authentifie avec un autre compte 6. Rejet : domaines non alignés
Parcours du message : du robot au rejet par le fournisseur SMTP
  1. Robot vers Application : Charge le formulaire de contact
  2. Robot vers Application : Le soumet une seconde plus tard
  3. Application vers Serveur mail : Envoie avec l’adresse de son domaine
  4. Serveur mail : Remplace l’expéditeur
  5. Serveur mail vers Fournisseur SMTP : S’authentifie avec un autre compte
  6. Fournisseur SMTP vers Serveur mail : Rejet : domaines non alignés

L’application demandait bien une adresse de son propre domaine. Mais une règle générale du serveur mail remplaçait l’expéditeur de tous les domaines qu’elle ne connaissait pas par une adresse par défaut. Le domaine de cette application n’y avait jamais été ajouté. Le message partait donc avec trois identités différentes : celle de l’application, celle de l’adresse par défaut, et celle du compte d’envoi. Le rejet du fournisseur était légitime.

Première cause : rétablir l’identité e-mail

Garder l’adresse de l’application

Le domaine de l’application est ajouté aux règles du serveur mail, qui conserve désormais son expéditeur. La règle est vérifiée avant tout redémarrage.

Un compte par domaine

Le serveur choisit le compte d’envoi selon le domaine de l’expéditeur. Chaque application hébergée garde sa propre identité. Les mots de passe restent dans des fichiers protégés, jamais dans le dépôt Git.

Une clé DKIM propre au domaine

Une clé dédiée signe les e-mails de l’application, et sa partie publique est publiée dans le DNS. Le service de signature refusait d’abord une clé privée trop accessible : ses droits ont été restreints.

SPF, DKIM et DMARC. SPF autorise les serveurs qui envoient pour le domaine, DKIM signe le message, DMARC vérifie que le domaine visible correspond aux identités authentifiées. La politique DMARC démarre en observation, avant de passer en quarantaine puis en rejet une fois tous les expéditeurs légitimes connus. Le test final, côté destinataire, donne SPF, DKIM et DMARC validés, avec le domaine de l’application partout.

Deuxième cause : qui a soumis le formulaire ?

Les journaux du serveur web permettent de reconstituer la visite : quelques pages chargées, puis le formulaire affiché et soumis presque aussitôt.

Pris un par un, aucun de ces indices ne prouve qu’il s’agit d’un robot. Pris ensemble, ils ne laissent guère de doute.

Pourquoi le jeton CSRF n’a rien bloqué ? Parce que ce n’est pas son rôle. Il empêche un autre site de faire agir un utilisateur à son insu. Un robot, lui, charge la page, lit le jeton et le renvoie avec sa soumission. Un jeton CSRF n’est pas une protection anti-robots.

  • Le formulaire, six champs, soumis environ une seconde après son affichage
  • Aucune feuille de style ni aucun script chargés par cette adresse
  • Une ancienne version du protocole HTTP, avec un navigateur moderne annoncé
  • Des valeurs saisies typiques d’un envoi automatisé

Des protections en couches, sans captcha

Un captcha ajoute un service externe, du JavaScript et de la friction pour les visiteurs. Pour un volume de spam raisonnable, trois protections invisibles suffisent, et le captcha reste une étape suivante si les robots deviennent plus sophistiqués.

Un champ que seuls les robots voient

Un champ supplémentaire, hors de la zone visible et du parcours clavier. Un humain le laisse vide, beaucoup de robots le remplissent.

Le temps de remplissage

Le moment où le formulaire est affiché est conservé côté serveur. Une soumission quasi instantanée d’un formulaire de six champs est écartée. Un champ caché dans la page ne suffirait pas : le visiteur peut le modifier.

Un nombre de tentatives limité

Une même source ne peut soumettre le formulaire qu’un nombre limité de fois sur une période. Au-delà, le serveur répond qu’il faut patienter.

Ne pas dire au robot qu’il a été détecté. Un champ piège rempli ou une soumission trop rapide ne déclenche aucun message d’erreur : le robot voit la page de confirmation habituelle, mais aucun e-mail ne part et la tentative est journalisée. Les contrôles s’enchaînent du moins coûteux au plus coûteux, pour écarter les robots simples avant même la limitation.

Une correction urgente, mais traçable

Même pressé, nous ne modifions pas les fichiers directement en production. Chaque changement passe par le dépôt, ce qui permet de le relire, de revenir en arrière et de garder le serveur identique au code.

  • Diagnostic sur le serveur de production
  • Correction dans le dépôt Git, jamais directement sur le serveur
  • Tests et relecture des changements
  • Déploiement, puis validation en production

Résultat

Chaque application envoie sous son propre domaine, avec son compte et sa signature. SPF, DKIM et DMARC sont validés.

Les soumissions automatiques simples sont écartées sans gêner les visiteurs, et chaque tentative est journalisée.

Serveur web, application et serveur mail se recoupent par l’heure : on sait qui a soumis quoi, et ce qui est réellement parti.

Toutes les corrections sont passées par le dépôt Git : relues, traçables et réversibles.

Ce que nous en retenons : quand un e-mail applicatif pose problème, suivre le message de bout en bout avant de corriger quoi que ce soit. Sans les journaux, l’incident aurait été pris pour une compromission du serveur mail. Et aucune protection anti-robots n’est absolue : c’est leur combinaison, avec les journaux, qui fait la défense.

Stack

Symfony / PHP Ubuntu Nginx Postfix OpenDKIM SPF, DKIM, DMARC

Questions fréquentes

Le jeton CSRF de Symfony protège-t-il un formulaire contre les robots ?

Non. Il empêche un autre site de faire agir un utilisateur à son insu. Un robot peut charger la page, lire le jeton et le renvoyer. Il faut des protections dédiées : champ piège, délai de remplissage contrôlé côté serveur, limitation des tentatives.

Que signifie un rejet « From header domain does not align with authenticated domain » ?

Le domaine visible de l’expéditeur ne correspond pas au compte utilisé pour envoyer le message. C’est souvent une réécriture d’adresse ou un compte d’envoi partagé entre plusieurs domaines sur le même serveur.

Comment savoir si mon serveur mail est compromis ?

En suivant le message dans les journaux du serveur mail : s’il a été produit sur le serveur lui-même, c’est une application locale qui l’a envoyé. Il reste alors à trouver laquelle, et pourquoi.

Faut-il ajouter un captcha à mon formulaire ?

Pas forcément en premier. Un champ piège, un délai de remplissage et une limitation des tentatives arrêtent la plupart des robots sans gêner les visiteurs. Le captcha reste une option si les robots deviennent plus sophistiqués. Vous pouvez tester la protection de votre formulaire.

Autres interventions et réalisations en sécurité

Authentification forte par SMS (OTP / 2FA)

Application Symfony avec accès à des données sensibles : mise en place d’une authentification à deux facteurs par codes OTP envoyés par SMS. Sécurisation par profil, rôle ou zone, intégration à l’existant.

Lire le cas

Incident critique et sécurisation d’un site WordPress

Site WordPress compromis après attaque : back-office bloqué, environnement potentiellement altéré. Nettoyage, restauration des accès, audit sécurité, mise à jour complète et durcissement pour réduire les risques de récidive.

Lire le cas

Clé API Stripe compromise et fausses factures

Environ 800 fausses factures envoyées depuis le compte Stripe d’une entreprise. Diagnostic par les journaux, clé remplacée, origine de la fuite trouvée, compte nettoyé, aucun paiement.

Lire le cas

Des e-mails rejetés ou un formulaire spammé ?

Décrivez-nous ce que vous constatez : nous suivons le message dans les journaux, trouvons la cause et corrigeons l’envoi et le formulaire. Voir aussi notre guide spam de formulaire : pourquoi et comment l’éviter, le test anti-spam de votre formulaire et notre audit de serveur.