le formulaire de commentaires avec anti spam

Comme je l'évoquais , je m'ammuse à coder un CMS de blog.

En regardant de plus pret les modules antispam de wordpress ou dotclear. (notemment Askimet). Bien qu'apparaissant à première vue comme des logiciels libres., ce sont des logiciels privateurs tel que le définit Richard Stalman. En effet, la plus part de ces modules soumettent le commentaire à un serveur pour approbation. Nous n'avons ni le code source utilisé, ni la maîtrise de cet intermédiaire.

Alors, le filtre se fait par 3 biais, une question simple et aléatoire qui permet de vérifier si l'utilisateur n'est pas un robot, une recherche dans les mots utilisés, et une blacklist.

La fonction commenter.

dans mon code, je l'appelle comme ça:

$c = comment_commenter($_GET['post'],$_POST['c_name'],$_POST['c_mail'],$_POST['c_site'],$_POST['c_content'],getenv("REMOTE_ADDR"),$_POST['c_human_reponse'],$_POST['spam_question_number']);

La blacklist

Hormis le test que tous les champs du formulaire obligatoires soient remplis, c'est la première chose testée. Une IP est blacklistée automatiquent au bout de n fails.. La date du blacklistage a pour but de pouvoir ajouter une purge de de la table. Pour tous les blacklistages de plus de 1 mois par exemple.

Les questions

C'est plus sympatique qu'un captcha glacial et chiant à taper, d'autant qu'on peut mettre un peu d'humour dans les questions. dans mon formulaire j'appelle la question comme ça:

<?php
 
$question=poserquestion();
echo "<I>".$question[1]."</I>";
?>
<input name="spam_question_number" size="30" maxlength="255" value="<?php echo $question[0]; ?>
" type="hidden">

la base de mots interdits

bah c'est juste un reg_match

les tables mysql pour que ce soit compréhensible

requête SQL: describe commentaires;
Enregistrements: 9

Field Type Null Key Default Extra
comment_id int(11) NO PRI NULL auto_increment
post_id int(11) NO   NULL  
ip text NO   NULL  
mail text NO   NULL  
commentaire text NO   NULL  
username text NO   NULL  
site text NO   NULL  
etat tinyint(1) NO   NULL  
comment_datetime datetime NO   NULL  

requête SQL: describe commentaires_ip;
Enregistrements: 8
Field Type Null Key Default Extra
ip char(15) NO PRI NULL  
fail_count int(11) NO   0  
last_comment_datetime datetime NO   NULL  
last_fail_cause text NO   NULL  
is_whitelisted tinyint(4) NO   0  
is_blacklisted tinyint(4) NO   0  
blacklist_cause text NO   NULL  
blacklist_datetime datetime NO   NULL  

requête SQL: describe commentaires_mots_interdits;
Enregistrements: 1
Field Type Null Key Default Extra
mot varchar(20) NO PRI NULL  

requête SQL: describe questions_spam;
Enregistrements: 3
Field Type Null Key Default Extra
question_id int(11) NO PRI NULL auto_increment
question text NO   NULL  
reponse text NO   NULL  

Le genre des questions:

requête SQL: SELECT * FROM `questions_spam` LIMIT 0, 30 ;
Enregistrements: 12
question_id question reponse
1 Si je retourne la lettre "E", ça donne quel chiffr... 3
2 trois plus cinq (reponse en chiffre) 8
3 le prénom de Jules Vernes c'est? Jules
4 La couleur du cheval blanc d'Henry4 blanc
5 il y a combien de lettres (non accentuees) dans l'... 26
6 avant d'avoir une reponse, on pose une... question
7 le contraire de non c'est oui
8 le contraire de oui c'est non
9 pincemi et pincemoi sont dans un bateau, pincemi t... pincemoi
10 Vous etes un humain derrière votre écran (oui/non) oui
11 il y a combien de bits dans un octet? 8
12 Lucien, l'auteur de http://www.puceau.nu est... puceau|informaticien|vierge

Le code source:


<?php
function poserquestion(){
//conexion mysql
include("config.php");
if (!mysql_connect($mysql_host, $mysql_user, $mysql_password)) {
echo 'Impossible de se connecter à MySQL';
exit;
}
mysql_query("USE ".$mysql_database);
 
//cette fonction retourne une question aléatoire et son numéro qui permettra de vérifier la réponse.
//pour l'aléatoire, j'ai besoin de savoir combien d'enregistrements va retourner la requete
$result=mysql_query("SELECT COUNT(*) FROM questions_spam");
$counter=mysql_fetch_row($result);
$count=(int)($counter[0]);
 
$result=mysql_query("SELECT question_id, question FROM questions_spam LIMIT ".rand(1, $count).", 1");
$row=mysql_fetch_row($result);
return array($row[0],$row[1]);
 
}
 
function verifierreponse($questionnum,$reponse){
//on interdit les réponses vides
if ($reponse==""){
return false;
}
 
 
 
// récupérer les réponses possibles
$result=mysql_query("SELECT reponse FROM questions_spam WHERE question_id='".$questionnum."'");
$row=mysql_fetch_row($result);
 
//dans l'enregistrement mysql, j'ai séparé losqu'il y avait plusieurs réponses possible par le caractère "|"
$reponses=explode("|",$row[0]);
 
foreach ($reponses as $rep){
if (($rep==$reponse)||(strtolower($rep)==strtolower($reponse))){
return true;
}
}
return false;
}
 
function comment_test_ip($ip){
 
 
//test black list
$result=mysql_query("SELECT
COUNT(*)
FROM
commentaires_ip
WHERE
ip='"
.$ip."'
AND is_blacklisted='1'"
);
$row=mysql_fetch_row($result);
if (!$row[0]=='0'){
return "blacklisted";
}
 
 
//test whitelist
$result=mysql_query("SELECT
COUNT(*)
FROM
commentaires_ip
WHERE
ip='"
.$ip."'
AND is_whitelisted='1'"
);
$row=mysql_fetch_row($result);
if (!$row[0]=='0'){
return "whitelisted";
}
return "RAS";
}
 
function comment_log_fail($err,$log_ip){
//compter les fails et blacklister au bout de la valeur définie dans le fichier de config
 
 
//est-ce que l'user a déja un enregistrement
$resultcount=mysql_query("SELECT COUNT(*) FROM commentaires_ip WHERE ip='".$log_ip."'");
$rowcount=mysql_fetch_row($resultcount);
If ($rowcount[0]=="1"){
 
//l'ip a déja été enregistrée on va faire un update
mysql_query("UPDATE commentaires_ip
SET
fail_count=fail_count+1,
last_comment_datetime=NOW(),
last_fail_cause='"
.$err."'
WHERE
ip='"
.$log_ip."'");
 
//on vérifie si les seuil entrainant une blacklist n'est pas dépassé
$result=mysql_query("SELECT fail_count FROM commentaires_ip WHERE ip='".$log_ip."'");
$row=mysql_fetch_row($result);
if ($row[0]==$comment_max_fails){
mysql_query("UPDATE commentaires_ip
SET
is_blacklisted='1',
blacklist_datetime=NOW(),
blacklist_cause='auto_blacklist'
WHERE ip='"
.$log_ip."'");
 
}
 
 
 
}else{
//l'ip n'a déja été enregistrée on va faire un INSERT
 
mysql_query("INSERT INTO
commentaires_ip(
ip,
fail_count,
last_comment_datetime,
last_fail_cause)
VALUES(
'"
.$log_ip."',
'1',
NOW(),
'"
.$err."')");
 
}
 
}
 
function comment_test_mots_interdits($texte){
$result=mysql_query("SELECT mot FROM commentaires_mots_interdits");
 
 
while($mots=mysql_fetch_row($result)){
 
if(preg_match("/\b".$mots[0]."\b/i", $texte)){
return array("erreur","Présence d'un mot interdit");
}
}
return array("OK","");
}
 
function comment_commenter($post_id,$pseudo,$mail,$site, $content,$ip, $humanreponse, $questionnumber){
//conexion mysql
include("config.php");
if (!mysql_connect($mysql_host, $mysql_user, $mysql_password)) {
echo 'Impossible de se connecter à MySQL';
exit;
}
mysql_query("USE ".$mysql_database);
 
//tester si les champs ont été remplis
if (($pseudo=="")||($mail=="")||($content=="")){
return array("erreur","Vous n'avez pas rempli le formulaire");
}
 
//tester l'IP
switch (comment_test_ip($ip)){
case "blacklisted":
return array("erreur","Votre adresse IP est blacklistée");
break;
default:
}
 
//tester la réponse à la question antispam
if (!verifierreponse($questionnumber,$humanreponse)){
//enregistrer le commentaire en tant que spam
mysql_query("INSERT INTO
commentaires(
post_id,
ip,
mail,
commentaire,
username,
site,
etat,
comment_datetime)
VALUES(
'"
.mysql_real_escape_string($post_id)."',
'"
.mysql_real_escape_string($ip)."',
'"
.mysql_real_escape_string($mail)."',
'"
.mysql_real_escape_string($content)."',
'"
.mysql_real_escape_string($pseudo)."',
'"
.mysql_real_escape_string($site)."',
'1',
NOW())"
);
 
comment_log_fail("fail on question test human",$ip);
return array("erreur","Vous avez mal r&eacute;pondu &aacute; la question pour v&eacute;rifier si vous &ecirc;tes un humain.");
}
 
//tester les mots interdits
$testcontent=comment_test_mots_interdits($content);
$testpseudo=comment_test_mots_interdits($pseudo);
 
if (($testcontent[0]=="erreur")||($testpseudo[0]=="erreur")){
echo "plop";
 
//enregistrer le commentaire en tant que spam
mysql_query("INSERT INTO
commentaires(
post_id,
ip,
mail,
commentaire,
username,
site,
etat,
comment_datetime)
VALUES(
'"
.mysql_real_escape_string($post_id)."',
'"
.mysql_real_escape_string($ip)."',
'"
.mysql_real_escape_string($mail)."',
'"
.mysql_real_escape_string($content)."',
'"
.mysql_real_escape_string($pseudo)."',
'"
.mysql_real_escape_string($site)."',
'1',
NOW())"
);
comment_log_fail("forbidden word",$ip);
return array("erreur","forbidden word.");
}
 
//enregistrer le commentaire
mysql_query("INSERT INTO
commentaires(
post_id,
ip,
mail,
commentaire,
username,
site,
etat,
comment_datetime)
VALUES(
'"
.mysql_real_escape_string($post_id)."',
'"
.mysql_real_escape_string($ip)."',
'"
.mysql_real_escape_string($mail)."',
'"
.mysql_real_escape_string($content)."',
'"
.mysql_real_escape_string($pseudo)."',
'"
.mysql_real_escape_string($site)."',
'0',
NOW())"
);
 
//réinitialiser le fail count
mysql_query("UPDATE commentaires_ip SET fail_count='0' WHERE ip='".$ip."'");
 
}
?>

Bon ok, sans exemple 'utilisation ce n'est pas terrible. Je publierai le code source du cms quand il sera opérationnel.

Commentaires

1. Le jeudi, février 24 2011, 09:48 par alarme

Merci pour ce code. Nous allons l'intégrer.

Page top