Protéger simplement un formulaire contre le spam en 2020

Si possible sans emmerder l’utilisateur et dépendre d’un système tiers

Il se trouve que j’ai dû récemment mettre en place une protection anti-spam sur un formulaire (parce que les dizaines de messages en russe par jour, bof), et que j’ai dû revoir mon panel de solutions sur ce qui fonctionne et qui ne fonctionne pas.

Cas d’utilisation

Le but du jeu ici de protéger un simple formulaire (type formulaire de contact et de commentaire) contre le spam de type « publicitaire » générique.

Il n’est pas question de se protéger contre la création de comptes multiples, contre une tentative de déni de service ou contre une attaque personnalisé – tout ça demande des moyens beaucoup plus complexes.

D’autre part, je veux respecter l’utilisateur et ça implique deux choses :

  1. Éviter de lui rendre pénible l’utilisation du formulaire en imposant des actions inutiles et chiantes comme « Déchiffrer des lettres distordues » ou « cliquer sur les passages piétons ».
  2. Éviter de lui faire charger douze tonnes de JS et d’envoyer toutes ses données dans la nature pour un simple formulaire.

Et enfin, je veux me simplifier la vie, en évitant d’avoir à créer des comptes, configurer des machins, installer des trucs spécifiques compliqués sur mes serveurs et dépendre d’acteurs tiers qui peuvent me couper le service (volontairement ou non) ou le rendre payant.

Ce qui ne fonctionne plus

Il y a encore quelques années, les robots spammeurs étaient idiots : ils lisaient le HTML de la page, détectaient le formulaire, remplissaient tout ce qu’ils pouvaient et envoyaient le tout. Il suffisait de créer un honeypot (« pot de miel ») en l’existence d’un champ caché. Si le formulaire était renvoyé avec ce champ rempli, c’est que l’envoi était du fait d’un robot (puisqu’un humain n’aurait pas vu le champ) (ou d’un malandrin malintentionné) et donc du spam.

Sauf que depuis est arrivée l’ère de la Single Page Application et de la page web qui ne fonctionne qu’avec Javascript activé. Donc, les robots de spam se sont modernisés et utilisent probablement des outils de pilotage de navigateur, type Selenium. La conséquence, c’est qu’ils ont la même « vision » de l’application que les utilisateurs réels, et donc que les champs cachés seront ignorés.

Mais alors, comment se prémunir de ce genre de spam ?

Est-on obligés de faire appel à des services tiers, comme semble l’indiquer l’immense majorité des ressources sur la question ?

Deux solutions simples et efficaces

Il y a deux techniques très efficaces que je m’étonne de les avoir vu que très rarement.

L’arme nucléaire : interdire les liens dans votre formulaire

Tous ces spams automatisés n’ont pour but que de vous faire cliquer sur des liens malveillants. Donc, s’il n’y a aucune raison légitime de mettre des liens dans votre formulaire, vous pouvez tout simplement…

Interdire les liens dans votre formulaire (et prévenir les utilisateurs de ce fait).

Une simple détection sur http:// ou https:// supprime 90 % de ceux que je reçois ; une version plus poussée qui détecte {texte}.{texte}/{texte} avec {texte} des bouts de textes sans espaces les détecte tous.

Plus subtil et un peu plus risqué

Une autre possibilité c’est de jouer sur le fait que les robots remplissent les formulaires rapidement. Trop vite en fait. Dans ceux que je reçois, c’est entre 300 et 1200 ms, avec une moyenne autour de 600 ms. Et donc…

Il suffit de mesurer le temps mis entre l’affichage du formulaire et son renvoi, les réponses trop rapides seront comptabilisées comme du spam.

Le plus simple, sans avoir à gérer de session, est d’envoyer une date (avec une précision à la seconde ou mieux) dans le formulaire. Le formulaire renvoie cette date sans la toucher, le serveur peut facilement calculer le temps écoulé.

Vous devez utiliser une horloge monotone pour cette utilisation, et pas l’horloge du système.

Une horloge monotone, c’est une horloge qui avance toujours au même rythme, quoi qu’il se passe. À la différence de l’horloge système, qui est généralement à l’heure légale ou à UTC. Or, cette heure bouge de façon non-linéaire : changements de fuseaux horaires, ajustements manuels, auto-ajustement avec NTP… et donc tout calcul précis de durée basé sur ce type d’horloge peut être faux et mener à des résultats aberrants.

Par exemple en Java, il faut utiliser System.nanoTime() et pas System.currentTimeMillis() (ni une nouvelle date, elles se basent sur ce dernier appel).

Limites

Les principales limites de ces systèmes sont :

  1. Aucune résistance à une attaque ciblée : l’attaquant qui a la patience de faire un script spécialement pour votre formulaire ne mettra pas longtemps à contourner les protections.
  2. Ça peut quand même ennuyer l’utilisateur ; soit s’il avait en fait une vraie raison de mettre un lien à laquelle vous n’avez pas pensée, soit si le temps de blocage du formulaire est trop court (pensez au cas où presque tout est pré-rempli par le navigateur et où l’utilisateur a prévu un copier/coller du texte principal).

Néanmoins en pratique ça couvre une bonne partie des cas d’usage, sans le moindre inconvénient ergonomique pour l’utilisateur.

Et c’est le principal.



Logo : une boite de spam, la viande en boite.

17 commentaires

Billet intéressant ! :)

Une autre idée, c’est d’essayer de détecter la langue du message.
Si le message est en anglais / chinois / etc et qu’on s’attend à une réponse en français, ça permet de faire un premier tri.

Par contre, à l’heure actuelle où la grande majorité des sites populaires utilisent le système ReCaptcha pour se protéger du spam, dans quelle contexte penses-tu qu’on puisse utiliser ces techniques ? Sur un blog perso, un site vitrine, son e-CV ?

Tiens tiens, coincidence ou pas ? Je viens de lire la newsletter de framasoft qui a eu le même problème (quoique, ce n’était probablement pas des robots).

Vague de SPAM

Une chose dont nous nous serions bien passé·es, c’est la vague d’arnaqueurs qui se sont épris de nos services pour diffuser leurs contenus. Nous avions déjà mis en place des contre-mesures sur des services sans inscription tels que Framapic ou Framalink, mais voilà que désormais les escrocs vont se créer des comptes à la main sur des services tels que Framaforms (pour publier 50 formulaires Watch Full Free Movie HD 2020).

Nous avons donc décidé de valider les demandes de nouveaux comptes Framaforms « à la main », le temps de trouver une solution plus pérenne (et moins fatigante pour nos salarié·es !). Avec plus de 30 000 formulaires en 3 jours de méfaits, il a fallu bien du travail pour faire le ménage !

Une autre idée, c’est d’essayer de détecter la langue du message.
Si le message est en anglais / chinois / etc et qu’on s’attend à une réponse en français, ça permet de faire un premier tri.

Green

C’est tentant mais difficile dès que l’alphabet est identique donc sujet à erreur, pour un résultat assez mauvais : j’ai du spam en français (à la louche, je dirais 50 % anglais, 30 % russe, 20 % français dans mon cas à mois, sur un nom de domaine français et une page déclarée en français).

Par contre, à l’heure actuelle où la grande majorité des sites populaires utilisent le système ReCaptcha pour se protéger du spam, dans quelle contexte penses-tu qu’on puisse utiliser ces techniques ? Sur un blog perso, un site vitrine, son e-CV ?

Green

Pour moi le système est efficace tant que ta préoccupation est de te protéger des simples robots, donc hors attaques ciblées, préoccupations de multi-comptes et protection anti force brute sur les mots de passe.

Il peut être délicat à mettre en place sur des formulaires où tu peux avoir des liens (ou du texte qui ressemble à des liens, comme du code) ou qui peuvent être remplis légitimement très rapidement.

Mais moi j’insiste, le honeypot trouve 100% des spams, même si j’en ai pas énormément.

Phigger

@Phigger tu aurais des détails sur la technique utilisée ? Parce que le mien en a bloqué exactement 0.

@firm1 : Pure coïncidence, je ne suis pas du tout l’actualité de Framasoft.

Une autre idée, c’est d’essayer de détecter la langue du message.
Si le message est en anglais / chinois / etc et qu’on s’attend à une réponse en français, ça permet de faire un premier tri.

Green

C’est tentant mais difficile dès que l’alphabet est identique donc sujet à erreur, pour un résultat assez mauvais : j’ai du spam en français (à la louche, je dirais 50 % anglais, 30 % russe, 20 % français dans mon cas à mois, sur un nom de domaine français et une page déclarée en français).

Et que penses-tu de filtrer par mots-clefs, comme "download", "free HD", etc ?
Si cette technique s’avère efficace, et si on y ajoute la détection de caractères russes, a priori cela règle déjà 80% du spam que tu reçois.

Ajouter à cela les techniques données dans ce billet, et on se retrouve avec un bon filtre je pense.

Bonjour,

J’ai un site multilingue, et je reçois du spam dans plein de langues, mais principalement en anglais quand même.

J’ai mis en place tout un truc pour protéger la page contact, et ça a l’air de marcher assez bien:

  • Pas plus de 2 liens, 3 si l’utilisateur est connecté, car parfois c’est quand même bien utile.
  • Un détecteur par mot-clé, qui ne te vire pas tout de suite à la première occurence pour éviter d’éventuels faux positifs pour rien. Comme dans le spam il y a une grande concentration de mots genre "sex" ou "money", ça permet quand même de virer ceux qui ont le moins d’imagination et qui réussiraient à passer à travers le reste.
  • Vérification du domaine de l’adresse e-mail indiquée par l’utilisateur
    • Son existance dans les DNS, pour jeter tous ceux qui prétendent avoir une adresse genre gdibufndbs@egiuefhdsfkg.com. Mine de rien la catégorie de spam "chat sur le clavier" sont aussi assez fréquents
    • Sa présence sur une liste de domaines d’hébergement d’e-mails jetables, genre yopmail, 10 minutes mail, etc. liste qu’il faut constamment mettre à jour soi-même malheureusement. Quelqu’un de légitime qui me contacte attendra généralement une réponse, donc il n’a aucune raison d’utiliser ce genre de service. A ce jour il n’y a pas loin de 500 domaines listés.
    • Sa présence dans une liste de domaines connus pour être des sources de spam, par requête DNS sur un service de lutte anti-spam.
  • Vérification de l’adresse IP de l’expéditeur pour voir si elle est connue pour être une source de spam, à nouveau par requête DNS.

En fait ça revient quasiment à faire les mêmes vérifications qu’un serveur mail. c’est compliqué, mais diablement efficace.

L’autre truc qui peut être important aussi, c’est ne pas donner de réponse clairement négative au spammeur comme quoi son message a été refusé, ou alors juste une erreur assez générique. Comme ça si un humain essaie de trouver une faille, il n’a que peu d’indices sur ce qui passe ou pas.

+0 -0

L’autre truc qui peut être important aussi, c’est ne pas donner de réponse clairement négative au spammeur comme quoi son message a été refusé, ou alors juste une erreur assez générique. Comme ça si un humain essaie de trouver une faille, il n’a que peu d’indices sur ce qui passe ou pas.

QuentinC

J’ai oublié de parler de ça, mais c’est clair que ça aide. Mon formulaire se comporte de la même façon pour l’utilisateur, que du spam ait été envoyé ou non. La différence c’est qu’en cas de détection de spam, le message n’est pas envoyé mais est stocké dans une table, où je peux aller vérifier à la main s’il n’y a pas des erreurs qui trainent.

Quant au reste de ton système @QuentinC y’a des choses qui me paraissent très lourdes et complexes à l’usage. Tu t’es amusé à mettre un système qui indique quels critères ont déclenché le classement en « spam » pour vérifier ce qui est utile ou non là-dedans ?

Tu t’es amusé à mettre un système qui indique quels critères ont déclenché le classement en « spam » pour vérifier ce qui est utile ou non là-dedans ?

Non, je ne me suis pas amusé à faire ça. Tout ce qui compte c’est que je ne reçoive pas de spam. Le reste je m’en fous. ET je n’ai encore pas eu de plainte comme quoi quelqu’un n’a pas réussi à envoyer un vrai message.

En réalité je n’ai pas mis en place tous les éléments en un seul coup, ça a été progressif. D’abord la limitation des liens et les mots-clés. Puis je me suis aperçu qu’il y avait les spam de type total random. ET ensuite bien plus tard, malgré tout ça, qu’il y en avait encore qui passaient.

Tu as peut-être raison, le truc par mot-clé est peut-être devenu complètement inutile maintenant.

LE tout premier test avant même de bloquer les liens était juste un cookie pour éviter les doublons trop rapprochés. Ca par exemple c’est l’exemple type des trucs qui ne marchent pas. Les bots s’en fichent totalement des cookies, mais au moment où on pond le truc on ne se rend pas compte de l’énormité.

La liste de domaines d’e-mail jetables, je ne l’utilise évidemment pas que là mais également à l’inscription. Je n’aurais certaienement pas mis ça en place juste pour un seul anti-spam.

+0 -0

Bonjour! Merci pour ce billet.

Je me demande si un simple champs du style 1+3= ? avec validation (soit JS soit backend) ne suffit pas pour les robots ? Ils sont capables de répondre à ce genre de question simple ? (Ou autre petit quizz du type de https://contactform7.com/quiz/ )

Ok ca demande un peu plus d’effort de l’utilisateur, mais ca reste minime. C’est uniquement pour cette raison que cette solution est écartée ?

Ok ca demande un peu plus d’effort de l’utilisateur, mais ca reste minime. C’est uniquement pour cette raison que cette solution est écartée ?

Gwend@l

Oui, le but c’est de ne pas imposer d’actions spécifiques à l’utilisateur tant que ça n’est pas nécessaire.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte