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.
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.
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.
Avant de toucher à la configuration, il fallait savoir laquelle était la bonne.
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.
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.
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.
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é 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.
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.
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 supplémentaire, hors de la zone visible et du parcours clavier. Un humain le laisse vide, beaucoup de robots le remplissent.
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.
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.
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.
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.
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.
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.
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.
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.
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 casSite 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 casEnviron 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 casDé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.