Filtrer le spam grâce à AbuseIPDB

Il est difficile de nos jours de publier un formulaire de contact ou pour commenter un article de blog sans être spammé constamment. Les pots à miel n’étant pas très efficaces, j’ai récemment testé une méthode qui permet de filtrer plus efficacement le spam en utilisant une base de données collaborative d’adresses IP : AbuseIPDB.

Un anti-spam, et bien plus encore

Cette base de donnée n’est pas réservée qu’au signalement de spam : les cyberattaques sont également cataloguées. Une intégration native à fail2ban est d’ailleurs possible pour signaler automatiquement les tentatives d’intrusion sur votre serveur.

La collaboration au service de la sécurité

Même si les spammeurs ont aujourd’hui des outils sophistiqués pour automatiser le remplissage et l’envoi de formulaire, ils ont encore tendance à le faire depuis les mêmes machines pendant un moment. Et c’est justement grâce à cette faiblesse que l’on peut les repérer : en effet, ils changent rarement d’adresse IP.

Lorsqu’une adresse est signalée plusieurs fois comme source de spam (ou d’attaque), il suffit alors de la bloquer pour éviter d’en être soi-même victime.

C’est là qu'AbuseIPDB joue un rôle important : il s’agit d’une base de donnée collaborative où chaque inscrit peut à la fois signaler des attaques, mais aussi interroger la base pour obtenir à la fois une liste des signalements pour une adresse IP mais également obtenir un score (calculé en fonction de plusieurs facteurs, pour éviter les abus et rendre ce chiffre aussi objectif que possible) indiquant la probabilité qu’une adresse soit nuisible.

Non seulement le site permet de réaliser ces actions manuellement (pratique si vous voulez par exemple vérifier que votre propre adresse n’a pas été utilisée pas le passé par exemple), mais ils proposent surtout une API Web pour interroger leurs données directement depuis votre site ou application.

Pour éviter les abus le site demande à la fois d’être inscrit pour signaler une IP, mais il faudra générer une clé d'API pour l’utiliser.

Attention aux limites

Les API sont soumises à des limites d’accès quotidiennes, en fonction de votre abonnement. La version gratuite est suffisante pour un site perso, mais il faudra probablement passer à un abonnement payant si vous avez un trafic important.

Vous pouvez également vérifier votre nom de domaine et publier un lien vers votre profil pour augmenter gratuitement les limites appliqués à votre compte.

Une fois votre compte créé et une clé d'API générée, vous pouvez commencer à implémenter un filtre anti-spam en quelques minutes sur votre site !

À propos des exemples dans cet article

Mon site utilise le package Laravel officiel pour utiliser plus simplement ces API, mais les exemples partagés ici seront développés en PHP simple. Vous pouvez bien entendu les adapter à n’importe quel langage (Node.js, Python, Java…) pour implémenter les mêmes fonctionnalités dans votre projet.

Récupérer l'adresse IP du client

Lorsqu’un client fait appel à votre formulaire, son adresse IP est accessible via les informations envoyés au serveur.

Laravel propose un raccourci via request()->ip(), mais hors de ce framework il faudra probablement récupérer cette information manuellement (ou trouver le raccourci proposé par votre outil de choix) :

$apiKey = 'Insérez votre clé ici';
$ip = $_SERVER['REMOTE_ADDR'];
Attention aux entêtes HTTP

Vous pouvez trouver d’autres bouts de code sur internet qui utilisent plutôt $_SERVER['HTTP_CLIENT_IP'], qui correspond à un entête HTTP. Celui-ci étant envoyé par le client il peut facilement être falsifié. Faites attention à utiliser une source d’information fiable lorsqu’il s’agit de sécuriser votre site.

Contrôler une adresse IP

Maintenant que vous avez récupéré l’adresse du client, vous pouvez interroger AbuseIPDB pour savoir si cette adresse a été utilisée pour des attaques récentes? J’utiliserai ici cURL pour rendre le code le plus universel possible, mais vous pouvez également utiliser un client HTTP de votre choix, par exemple Guzzle, pour simplifier votre code.

$ch = curl_init('https://api.abuseipdb.com/api/v2/check?ipAddress=' . urlencode($ip));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
	'Key: ' . $apiKey,
	'Accept: application/json'
]); 
$data = curl_exec($ch);
curl_close($ch);

$result = json_decode($data);

Avec le paquet pour Laravel, vous pouvez utiliser AbuseIPDB::check($request->ip(), 30); pour obtenir le même résultat.

La donnée qui va nous intéresser ici est contenue dans la clé abuseConfidenceScore de l’objet data retourné. Il s’agit d’un pourcentage de confiance, 100 représentant une certitude que l’adresse est nuisible.

Libre à vous d’utiliser le pourcentage que vous souhaitez comme limite pour bloquer une requête. J’utiliserai ici 75 pour bloquer les spammeurs potentiels.

if ($result->data->abuseConfidenceScore >= 75) {
	http_response_code(403);
	die('Spam détecté');
}

Avec Laravel pour pouvez renvoyer une erreur 403 et la page appropriée avec abort(403, 'Spam détecté');.

N’oubliez pas l’assurance qualité

Pensez à proposer une page d’erreur personnalisée pour expliquer à vos utilisateurs les raisons de ce filtrage. Les faux positifs sont vite arrivés, même avec une limite de filtrage haute. 😉

Signaler un abus

Bien entendu, la même API permet également de signaler une adresse IP lorsque vous êtes victime d’une attaque.

Sur mon site par exemple j’enregistre l’adresse IP utilisée pour soumettre un formulaire public. Cela me permet ensuite de signaler les contrevenants qui n’auraient pas été directement filtré (par exemple lorsqu’ils utilisent une adresse considérée comme fiable).

Attention lorsque vous enregistrez une adresse IP. Comme il s’agit d’une donnée personnelle, le RGPD est très strict quant à son traitement.

Cette fois il suffit d’envoyer une requête POST au bon endpoint :

$ch = curl_init('https://api.abuseipdb.com/api/v2/report');
curl_setopt($ch, CURLOPT_POST, 1); 
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
	'ipAddress' => $ip,
	'categories' => '10', // Cf. https://www.abuseipdb.com/categories pour choisir les bons identifiants, séparés par une virgule
	// Vous pouvez ajouter les paramètres facultatifs `comment` et `timestamp` si besoin
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
	'Key: ' . $apiKey,
	'Accept: application/json'
]); 
$data = curl_exec($ch);
curl_close($ch);

Cette requête renverra le score abuseConfidenceScore mis à jour, si cette donnée vous intéresse.

Avec le paquet Laravel, ce code peut être résumé par AbuseIPDB::report($request->ip(), [10]);.

Supprimer un signalement

Votre signalement étant lié à votre compte (via la clé d’API utilisée), vous pouvez le retrouver sur votre compte pour le supprimer en cas d’erreur (par exemple si vous avez signalé votre propre adresse pendant vos tests). Vous pouvez également effectuer cette action via l’API bien entendu.

Aller plus loin

N’hésitez pas à parcourir la documentation d’API d’AbuseIPDB pour en savoir plus sur les paramètres acceptés et découvrir d’autres usages possibles.

Vous pouvez par exemple l’utiliser pour lister les signalements faits sur votre adresse IP pour vous assurer que vous n’avez pas été signalé par quelqu’un d’autre (ce que j’ai fait sur l’interface d’administration de mon site).

Vous pouvez utiliser des intégrations ou plugins officiels dans les outils que vous avez déjà mis en place. Par exemple fail2ban peut signaler automatiquement les tentatives d’intrusion répétées en ajoutant quelques lignes à sa configuration.

Bonus : un middleware Laravel prêt à l'usage

Si comme moi vous utilisez Laravel pour votre projet, je vous partage mon middleware prêt à l’usage qui permet de filtrer et signaler les IP classées comme spam :

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use AbuseIPDB\Facades\AbuseIPDB;
use App\Enums\SpamType;

class FilterSpamIp
{
    public static $reportCategories = [
        'blog-comment' => [10, 12],
        'contact-form' => [10],
    ];

    public static $reportComments = [
        'blog-comment' => 'Blog comment spam attempt',
        'contact-form' => 'Contact form spam attempt',
    ];

    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next, string $type = SpamType::ContactForm): Response
    {
        $checkResponse = AbuseIPDB::check($request->ip(), 30);

        if ($checkResponse->abuseConfidenceScore > 75) { // Plausible spam IP address
            $reportCategories = collect(self::$reportCategories)->get($type, [10]);
            $reportComment = collect(self::$reportComments)->get($type, 'Web spam attempt');

            AbuseIPDB::report($request->ip(), $reportCategories, $reportComment);

            abort(403, 'Spam IP detected');
        }

        return $next($request);
    }
}

La version originale de cet article est disponible sur mon blog.

4 commentaires

Merci pour le billet !

Est-ce que le score d’une IP peut s’améliorer dans le temps (plutôt que de se détériorer), par exemple si les signalements ne sont plus pris en compte dans le calcul après X mois ?

Si je comprends bien, le bout de code sur le signalement d’abus est relié à un bouton d’administration du genre "Signaler", donc nécessite un jugement humain ?

N’y aurait-il pas des moyens de détecter de manière automatisée des abus en aval du filtrage : multi-compte ? fréquence ou nombre de poste ? vérification du contenu du message ?

Oui le score s’améliore avec le temps, s’il n’y a plus de signalement. J’ai plus le détail en tête mais de mémoire le calcul est expliqué dans la FAQ d’AbuseIPDB (il y a de la pondération en fonction du temps, du type de compte qui signale, de la fréquence de signalement…). ;) (EDIT Juste là)

Le signalement peut être fait à n’importe quel moment. Perso je l’ai intégré à deux endroits, avec des catégories distinctes :

  1. automatiquement en cas de spam détecté par mon algo maison (je garde mon propre historique de spam interne auquel comparer)
  2. manuellement au cas où un spammeur passe à travers les mailles du filet (auquel cas je signale et je garde en base pour alimenter mon algo local)

Ça permet de signaler automatiquement la majorité des cas, mais j’ai toujours la main pour les quelques cas non détectés.

J’ai pas eu d’abus sur la création de compte sur mon site, mais effectivement je détecte certaines choses. Mais je révèle pas tous les détails ici car c’est propre à mon site, autant laisser chacun adapter le code à son besoin :)


Ah tiens, la dernière section a pas été publiée, j’ai du faire une fausse manip’, il manque le middleware Laravel que j’ai développé. J’ajoute ça tout de suite !

Intéressant :)

Oui le score s’améliore avec le temps, s’il n’y a plus de signalement. J’ai plus le détail en tête mais de mémoire le calcul est expliqué dans la FAQ d’AbuseIPDB (il y a de la pondération en fonction du temps, du type de compte qui signale, de la fréquence de signalement…). ;) (EDIT Juste là)

viki53

Comme les spammeurs ont accès eux aussi à l’API et aux règles, j’imagine qu’ils peuvent essayer d’en tirer profit ou de trouver la faille. Par exemple, ils pourraient essayer de contrôler leur score pour rester en dessous d’une certaine limite. Ou encore ils pourraient lister les sites soupçonnés d’utiliser abuseipdb en testant avec des IP avec un score élevé pour voir si les commentaires seraient validés ou non (ou en voyant leur score s’incrémenter). Enfin c’est le jeu du chat et de la souris ^^

+0 -0

Ils pourraient limiter leur score oui, mais ça impliquerait de moins spammer (en encore, pour les sites sur lesquels ils savent qu’AbuseIPDB est utilisé) et d’admettre un score à ne pas dépasser… autant changer régulièrement d’IP à ce compte-là, mais ça coûte vite cher.

Dans tous les cas je doute qu’ils investissent tant que ça pour passer à travers les mailles du filet, quand on voit la qualité du contenu qu’ils postent :D

En attendant j’ai mis en place une méthode qui limite pas mal le spam que je reçois sur mon site (en commentaires sur le blog et via le formulaire de contact)… et je suis bien content vu que ça m’a pas pris bien longtemps à déployer :)

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