L'anti-spam fastoche

On veut être web2.0, on veut de l'interaction, du commentaire, de la réponse et paf on se ramasse 150 messages vantant les mérites du Viagra ou je ne sais quel autre médicament faisant la fortune de laboratoires américains. Pourtant on ne veut pas utiliser de CAPTCHAs parce que ce n'est pas terrible niveau accessibilité, les formules mathématiques semblent séduisantes mais pas merveilleuses, et les webservices comme Akismet trop lourds… Alors quoi ?

Après avoir lu un article fort intéressant sur le blog du Snook j'ai écris une fonction assez simple et qui se révèle particulièrement efficace. Comme je suis drôlement sympa, je la partage avec vous.

Permis à point

Cette fonction utilise un système à points comme décrit dans l'article de Snook. Pour tout ce qui parait bien, des points sont accordés, et pour tout ce qui est moins bien des points sont retranchés. Plus c'est suspicieux et plus les points s'envolent. La meilleure note étant 4, ça tombe très vite :

  • le score est de 1 ou plus, on peut considérer le message comme valide.
  • le score est à zéro on peut suspecter un spam, mais rien d'alarmant, on le classera dans les messages à modérer.
  • le score est inférieur à zéro, il s'agit certainement d'un spam
  • le score est inférieur à 10, ce n'est même pas la peine de s'embêter à enregistrer ce message, il pue le spam.

Homme ou machine (nul n'imagine quel est son secret)

La fonction vise surtout les spams automatiques, elle ne pourra pas grand chose contre les spams écrits par de vilaines gens. Mais enfin, le spam humain ne représente pas grand chose vu le cout de la main d'œuvre, alors dormons presque tranquille.

<?php

function wd_spamScore($body$url$author$words=NULL$starters=NULL)
{
    #
    # score >= 1 - The message doesn't look like spam
    # score == 0 - The message should be put to moderation
    # score < -10 - The message is most certainly spam
    #
    
    $score = 0;
    
    #
    # put our body in lower case for checking
    #
    
    $body = strtolower($body);
    
    #
    # how many links are in the body ?
    #
    
    $n = max
    (
        array
        (
            substr_count($body'http://'),
            substr_count($body'href'),
            substr_count($body'ftp')
        )
    );

    if ($n > 2)
    {
        #
        # more than 2 : -1 point per link
        #
    
        $score -= $n;
    }
    else
    {
        #
        # less than 3 : +2 points
        #
        
        $score += 2;
    }

    #
    # now remove links
    #

    # html style: <a> <a/>

    $body = preg_replace('#\<a\s.+\<\/a\>#'NULL$body);

    # bb style: [url] [/url]

    $body = preg_replace('#\[url.+\/url\]#'NULL$body);

    # remaining addresses: http://

    $body = preg_replace('#http://[^\s]+#'NULL$body);

    #
    # how long is the body ?
    #
    
    $l = strlen($body);
    
    if ($l > 20 && $n = 0)
    {
        #
        # More than 20 characters and there's no links : +2 points
        #
        
        $score += 2;
    }
    else if ($l < 20)
    {
        #
        # Less than 20 characters : -1 point
        #
        
        $score--;
    }
    
    #
    # Keyword search
    #
    
    if (empty($words))
    {
        $words = array();
    }
    
    $words += array
    (
        'levitra''viagra''casino''free sex''porn'
    );
    
    foreach ($words as $word)
    {
        $n = substr_count($body$word);
        
        if (!$n)
        {
            continue;
        }
        
        $score -= $n;
    }
    
    #
    # URL length
    #
    
    if (strlen($url) > 32)
    {
        $score--;
    }
    
    #
    # Body starts with...
    #
    
    if (empty($starters))
    {
        $starters = array();
    }
    
    $starters += array
    (
        'interesting''sorry''nice''cool''hi'
    );
    
    foreach ($starters as $word)
    {
        $pos = strpos($body$word . ' ');
        
        if ($pos === false)
        {
            continue;
        }
        
        if ($pos > 10)
        {
            continue;
        }
    
        $score -= 10;
            
        break;
    }
    
    #
    # Author's name has 'http://' in it
    #
    
    if (strpos($author'http://'))
    {
        $score -= 2;
    }
    
    #
    # How many different words are used ?
    #
    
    $count = str_word_count($body);
    
    if ($count < 10)
    {
        $score -= 5;
    }
        
    return $score;
}

?>

Comment je fais ? (en réponse à Hureau)

Avant de sauver un message dans notre base de données, on appelle la fonction et, en se référant au système à points, on décide de son sort. C'est tout simple, vraiment. Imaginons un formulaire avec les champs author, url et message:

<?php

$score = wd_spamScore($_POST['message']$_POST['url']$_POST['author']);

if ($score > 0)
{
    #
    # on sauve le message
    #
}
else if ($score == 0)
{
    #
    # on sauve le message avec un drapeau de modération
    #
}
else
{
    #
    # je ne m'ennuie même pas avec les scores négatifs,
    # je mets le message à la poubelle
    #
}

?>

Aller plus loin

Cette fonction est très efficace, mais c'est pas le délire non plus. Le développeur sexy et ambitieux aura vite fait d'étendre la vérification avec une jolie base de donnée pour vérifier si les messages précédents de l'auteur ont été validés ou non, ou si le corps du message est déjà utilisé ailleurs… Mais enfin, si comme moi vous ne voyez plus l'ombre d'un vilain spam sur votre site pas la peine de se prendre la tête :-D

4 commentaires

1
Hureau a écrit : Mardi 01 Avril 2008 à 13:51

Très intéressé étant régulièrement spammé. Mais étant newbie en php, je le mets où ce code par rapport à mon formulaire ??? D'avance merci Xavier Hureau

2
Olivier a écrit : Mardi 01 Avril 2008 à 21:15

J'ai ajouté une section « Comment je fais ? ». J'espère qu'elle répond à ta question.

3
007 a écrit : Mardi 12 Août 2008 à 14:51

Encore un article intéressant par sa simplicité et son 'work around', j'adopte cette méthode très prochainement. Merci !

4
Alphanono a écrit : Jeudi 11 Septembre 2008 à 17:20

Hello … y'en a qui aiment se triturer les méninges !! Bon, ça semble intéressant. Reste à voir si l'attribution des points est bonne. Pour ma part, dans ce genre de formulaire je crée un champ texte que je masque avec du css. Les robots (ces cons !) remplissent immanquablement ce champ alors qu'un humain ne le voit même pas. Résultat, le formulaire n'est pas pollué par des CAPCHA avec tout pleins de calculs à faire ou des lettres à lire (pfiou … trop dur) et il suffit de vérifier si le champ spécial a été rempli ou non.

Laisser un commentaire

 
 Souhaitez-vous être informé par E-Mail de la réponse à votre message ?