Un plugin complémentaire de NoSPAM, qui bloque les soumissions de formulaire remplis de texte
aléatoire, du genre azVIkQyEgyGNYAcEyLcDI ou gfaabbVsLbqeodWX.
C’est ce que produit un robot qui remplit un formulaire sans le comprendre : il obtient un
jeton valide en chargeant la page, évite le champ piège masqué en CSS, et poste du charabia
dans tous les champs texte.
Aucune friction pour l’internaute : pas de captcha, pas de case à cocher, pas de service tiers, rien à charger dans la page.
Principe
Une valeur est retenue comme charabia si elle est un seul mot d’au moins 8 lettres non accentuées, sans espace, et qu’elle porte l’un des trois motifs qu’aucune langue à alphabet latin ne produit :
- au moins trois sauts de casse à l’intérieur du mot :
gfaabbVsLbqeodWX,azVIkQyEgyGNYAcEyLcDI. Une raison sociale composée en porte un ou deux —EcoMobile,InstitutAgroSud,FromagerieDuCausse— là où une chaîne tirée au hasard en produit cinq en médiane ; - ou huit consonnes consécutives dans un mot pauvre en voyelles (moins de 42 %) :
fghjfghkj,jnklnkjnjknjkn, le doigt qui traîne sur le clavier ; - ou une même lettre répétée quatre fois :
dddddddddd,xxxxxxxx.
Le taux de voyelles ne s’applique qu’au motif des consonnes : azVIkQyEgyGNYAcEyLcDI
en compte 48 %, y inclus, tout en portant sept sauts de casse.
La soumission est refusée à partir de deux champs marqués.
Calibration
Les seuils ne sont pas choisis au jugé. Ils sont calés sur deux corpus réels de réponses de formulaire SPIP, 530 000 en tout, en cherchant à chaque fois le réglage qui ne refuse aucune contribution humaine :
| réglage | ce qu’il bloque en plus | ce qu’il prend à tort |
|---|---|---|
| 2 sauts de casse | 16 réponses | FromagerieDuCausse, saisi dans deux champs par une entreprise de ce nom : 2 soumissions réelles refusées |
| 3 sauts (défaut) | rien | |
| 5 consonnes consécutives | 25 réponses | un patronyme germanique saisi dans deux champs : 2 soumissions réelles refusées. Et toute une famille de noms à un champ |
| 7 consonnes | 2 réponses | Hirschsprung, Schtschedrin : les patronymes les plus denses atteignent exactement sept |
| 8 consonnes (défaut) | rien |
C’est la leçon principale de la calibration : le faux positif d’un tel filtre, c’est un patronyme. Refuser un formulaire à quelqu’un à cause de son nom de famille n’est pas acceptable, et cela se produit à des seuils qui paraissent pourtant raisonnables.
Résultat avec les réglages par défaut :
| corpus | réponses | bloquées | faux positifs |
|---|---|---|---|
| site A | 4 063 | 64, toutes du spam | 0 |
| site B | 525 577 | 1 083 (0,21 %) | 0 |
Sur le site A, les 64 réponses bloquées sont l’intégralité du spam reçu, sur quinze formulaires et quatre vagues étalées sur dix mois : aucun faux négatif non plus. Sur le site B, chaque valeur marquée apparaissant plusieurs fois a été inspectée à la main : robots, scanners de vulnérabilité, et saisies de test laissées par des humains qui essayaient le formulaire.
Ce que ce plugin ne fait pas
Il ne détecte que ce motif-là. Un spam rédigé en langue naturelle — mots de dictionnaire concaténés, texte produit par un modèle de langue — passe sans être vu, et c’est par construction.
C’est pourquoi chaque soumission évaluée crée une ligne de log, y compris celles qui passent : c’est la seule façon de voir un bot changer de méthode.
Deux catégories de soumissions ne sont jamais évaluées, à connaître avant de croire un formulaire couvert :
- les formulaires à étapes de Saisies. Chaque étape ne poste qu’une partie des champs, et la vérification n’en voit jamais l’ensemble : le plugin passe son tour plutôt que de juger sur un fragment. Un tel formulaire n’est donc pas protégé du tout ;
- les soumissions d’un administrateur connecté (
0minirezo), quels que soient les champs.
Le plugin complète nospam, il ne le remplace pas : jeton, champ piège, listes d’IP et modération des forums restent son affaire. Quand nospam est installé, un refus lui signale en retour l’IP du visiteur, que nospam met alors de côté 24 h pour ses propres formulaires : le refus déborde donc le formulaire où il s’est produit.
Installation
Le plugin ne nécessite aucune configuration une fois activé.
Si nospam est installé, le plugin reprend sa liste de formulaires : tout ce qui lui est
déjà confié est couvert, y compris les formulaires du plugin Formidable, qui s’y inscrivent
d’eux-mêmes. Sinon, la liste par défaut est formidable, inscription, forum,
ecrire_auteur, signature, recommander.
Si saisies est installé, la déclaration des champs du formulaire fait foi pour décider quoi analyser. C’est elle, et elle seule, qui dit qu’un champ est un mot de passe ou une adresse email. À défaut, le plugin retombe sur un filtrage par nom de champ.
Surveiller un formulaire de plus
Quand nospam est installé, passer par son pipeline habituel
nospam_lister_formulaires : le formulaire hérite du même coup de son jeton et de son champ
piège à robots.
function monplugin_nospam_lister_formulaires($formulaires) {
$formulaires[] = 'mon_formulaire';
return $formulaires;
}
Sinon, sans nospam, utiliser le pipeline nospam_charabia_lister_formulaires :
function monplugin_nospam_charabia_lister_formulaires($formulaires) {
$formulaires[] = 'mon_formulaire';
return $formulaires;
}
Écarter un champ de plus de l’analyse
Certains champs ont le droit de contenir ce qui ressemble à du charabia : un mot de passe, un
identifiant, une référence. Le détecteur ne les regarde pas : le plugin connaît déjà une
trentaine de fragments de nom qui les signalent, listés dans
nospam_charabia_fragments_exclus(), et la déclaration Saisies fait foi quand elle est là.
Pour ajouter un champ propre à un site, on peut utiliser le pipeline du même nom :
function monplugin_nospam_charabia_fragments_exclus($fragments) {
$fragments[] = 'matricule';
return $fragments;
}
Un fragment n’est reconnu que s’il occupe un mot entier du nom du champ : au début du nom ou
après un souligné, à la fin ou avant un souligné ou un chiffre. En déclarant matricule, les
champs matricule, matricule_2 et mon_matricule passent donc à travers le détecteur ;
matriculeur, lui, reste analysé normalement.
Un changement de casse sépare les mots au même titre qu’un souligné, pour les formulaires qui
nomment leurs champs en casse mixte : userPassword, monEmail et urlHTTPSite passent eux
aussi à travers le détecteur.
Le but de cette délimitation : les fragments sont courts et se cachent dans des mots courants.
Sans elle, lien retirerait client de l’analyse, tel retirerait hotel,
site retirerait necessite, pass retirerait passerelle, etc. Un robot pourrait alors
remplir ces champs de ce qu’il veut sans que rien ne soit analysé.
Configurations personnalisées
À poser dans config/mes_options.php. Aucune n’est nécessaire au fonctionnement.
| constante | défaut | rôle |
|---|---|---|
_NOSPAM_CHARABIA_MODE |
'bloque' |
'log' journalise sans jamais refuser |
_NOSPAM_CHARABIA_SEUIL |
2 |
champs marqués à partir desquels on refuse |
_NOSPAM_CHARABIA_LONGUEUR_MIN |
8 |
longueur minimale d’une valeur analysée |
_NOSPAM_CHARABIA_TRANSITIONS |
3 |
sauts de casse exigés ; 0 désactive |
_NOSPAM_CHARABIA_CONSONNES |
8 |
consonnes consécutives ; 0 désactive |
_NOSPAM_CHARABIA_REPETITIONS |
4 |
répétitions d’une même lettre ; sous 3, désactive |
_NOSPAM_CHARABIA_VOYELLES |
0.42 |
taux de voyelles maximal, sur le seul motif des consonnes |
Sur un site dont on ne connaît pas encore les contributions, commencer avec
_NOSPAM_CHARABIA_MODE sur 'log' quelques semaines et relire le journal avant de bloquer. Un
site qui reçoit des références, des codes ou des identifiants dans des champs texte libre
mérite ce temps d’observation.
Ne pas descendre _NOSPAM_CHARABIA_SEUIL à 1, ni _NOSPAM_CHARABIA_CONSONNES en dessous de 8,
ni _NOSPAM_CHARABIA_TRANSITIONS en dessous de 3 : voir le tableau de calibration.
Avec _NOSPAM_CHARABIA_REPETITIONS en dessous de 3, le motif ne distingue plus rien, il prend
Association et Marseille à 2, et absolument tout mot à 1. Une valeur inférieure à 3 est
donc ramenée d’office à 0, plutôt que d’appliquer un seuil que personne n’a demandé. Les
trois autres motifs continuent d’opérer, et xxxxxxxx reste attrapé par celui des consonnes.
Le message d’erreur est volontairement générique, et identique à celui de nospam, pour qu’un
robot ne puisse pas déduire quel filtre l’a arrêté. Pour y ajouter une adresse de contact,
surcharger la chaîne dans le lang/ du site :
// lang/nospam_charabia_fr.php
return ['erreur' => 'Impossible de prendre en compte votre message. Écrivez-nous à …'];
Journal
tmp/log/nospam_charabia.log, une ligne JSON par soumission évaluée :
{
"form": "formidable",
"n": 5,
"total": 10,
"seuil": 2,
"mode": "bloque",
"refuse": true,
"champs": [
"input_1",
"input_2",
"input_3",
"input_4",
"textarea_1"
],
"ip": "…",
"ua": "…",
"valeurs": {
"input_1": "gfaabbVsLbqeodWX",
"input_2": "CqjIOclCOCdEWRazK",
"input_3": "XkTScYIYOcCsrIAGFsinSb",
"input_4": "ztqIpOyEgqxOgagQECIM",
"textarea_1": "vatnFctyxweuBidLjdz"
}
}
Seules les valeurs des champs marqués y figurent, une entrée par nom listé dans champs,
tronquée à 60 caractères : c’est du charabia par construction. Celles des autres champs sont
des données personnelles d’internautes, elles ne sont jamais écrites. Un champ à valeurs
multiples étant marqué dès qu’une seule de ses valeurs l’est, seule celle-là est écrite, ses
voisines restent des saisies d’internaute.
ip et ua suivent la même règle : ils n’apparaissent que sur les lignes portant au moins
un champ marqué. Une soumission propre laisse bien une ligne, avec "n": 0, mais rien qui
désigne son auteur. Un site public journalise ainsi les robots, pas ses visiteurs.
Une soumission déjà refusée par nospam est tout de même analysée et journalisée, avec
"deja_refuse": true. Son refuse reste false : le message d’erreur affiché est celui du
premier rempart, celui-ci n’y touche pas. C’est ce qui permet de voir un robot arrêté ailleurs
commencer à changer de méthode.
No discussion
Add a comment
Avant de faire part d’un problème sur un plugin X, merci de lire ce qui suit :
Merci d’avance pour les personnes qui vous aideront !
Par ailleurs, n’oubliez pas que les contributeurs et contributrices ont une vie en dehors de SPIP.
Follow the comments:
|
