YBET

Rue Albert 1er, 7

6810 Pin - Chiny

Route Arlon - Florenville

(/fax: 061/32.00.15

Formations Webmaster et développement internet en ligne

YBET informatique Forum webmaster Créer son site soi-même

22. Corrections des entrées d'annonces, sécurité dans les formulaires

1. Caractères réservés - 2. Administration des mots interdits - 3. Tests et enregistrements des annonces avec mots interdits - 4. Conclusion

Au chapitre 19, nous avons créé le formulaire pour insérer des annonces sur Internet. Pourtant, nous ne pouvons pas le mettre sur un site "en production" tel quel. Aucune sécurité n'est associée aux données rentrées. Un utilisateur standard ne posera pas de problèmes, quelques spécimens, oui.

En premier, l'utilisation des 2 caractères réservés ' et " dans les annonces va envoyer un messages d'erreur au niveau des requêtes MySQL. Cette solution va utiliser la fonction ADDSLASHES($variable) dans l'insertion des annonces et STRIPSLASHES($variable) dans l'affichage. Ces 2 fonctions vont être utilisés dans TOUS les champs utilisateurs.

En deuxième, une spécialité de quelques webmasters est de spamsmer les sites de ce type (forum, livres d'or et ... sites de petites annonces). Les annonces risquent vite de reprendre des publicités pour des médicaments, casinos ou autres, liens sauvages sans rapport. D'autres termes peuvent également apparaître comme des termes injurieux. Certaines annonces peuvent également être vue comme de la calomnie vis à vis d'un site, d'une personne ou d'une marque. Au niveau juridique, le webmasters est responsable du contenu de votre site.

  1. Dans le premier cas, nous allons créer une table reprenant les mots interdits. Cette solution n'est pas parfaite puisque les spamsmeurs vont utiliser toutes les possibilités pour essayer d'insérer leurs mots clés, y compris avec des caractères de séparation entre les lettres des mots. A l'insertion des annonces, nous allons juste tester si ces mots sont repris. Si le mot apparaît, l'annonce est automatiquement supprimée.
  2. Le deuxième cas est difficile à repérer en automatique. Selon certains termes, nous enverrons un mail à l'administrateur reprenant l'annonce pour vérification manuelle. Un texte de type : "Vend ordinateur de marque ZZZZ, la poubelle des PC" est un terme injurieux pour la firme ZZZZ. Par contre, "Poubelles contenance 1000 litres à vendre" est une annonce standard.

En troisième, les spamsmers vont insérer des liens dans 1 ou plusieurs case à remplir (y compris le prix ...). Un lien Internet en HTML est codé sous forme:

Nous allons systématiquement supprimer ces liens en gardant le contenu. Ceci peut-être nécessaire pour certaines annonces. De toute façon, les liens sauvages reprennent dans la majorité des cas des termes ... de la liste interdite et les annonces seront automatiquement supprimées

En dernier, on va retrouver dans le corps du message des adresses mail, même si elle est reprise dans la fiche de l'utilisateur. Ces adresses "sauvages" sont généralement insérées par celui qui poste l'annonce par facilité. Par contre, ces adresses sont utilisées par d'autres robots pour envoyer des courriers indésirables. Deux solutions sont possibles, soit supprimer automatiquement l'adresse, soit remplacer le caractère @ par un autre mot.

Pour chaque cas, un mail va être envoyé à l'administrateur du site reprenant les coordonnées de celui qui poste. Ceci pour permettre par exemple de lui interdire manuellement de poster de nouvelles annonces.

L'ensemble de ces fonctions vont être reprises dans l'insertion d'annonces vues au chapitre 20. Par facilité, nous découperons cette partie.

2. Caractères réservés.

Dans entree-annonce, nous allons modifier:

$titre = $_POST['titre'];
$erreur_titre = "";
$description = $_POST['description'];
$erreur_description = "";
$ville = $_POST['ville'];
$erreur_ville = "";
$pays = $_POST['pays'];
$erreur_pays = "";
$prix = $_POST['prix'];
$erreur_prix = "";
$ville = $_POST['telephone'];
$erreur_telephone = "";
$erreur_file = "";
$uid_cat=$_POST['uid_cat'];

$dateinsertion=date('Ymd');

$titre = ADDSLASHES($_POST['titre']);
$erreur_titre = "";
$description =ADDSLASHES($_POST['description']);
$erreur_description = "";
$ville = ADDSLASHES($_POST['ville']);
$erreur_ville = "";
$pays = ADDSLASHES($_POST['pays']);
$erreur_pays = "";
$prix = ADDSLASHES($_POST['prix']);
$erreur_prix = "";
$telephone = ADDSLASHES($_POST['telephone']);
$erreur_telephone = "";
$erreur_file = "";
$uid_cat=$_POST['uid'];
$dateinsertion=date('Ymd');

Nous avons simplement inséré la fonction ADDSLASHES pour chaque variable.

Dans la partie affichage liste, nous utiliserons la fonction inverse: STRIPSLASHES($variable) Cette partie du développement ne pose normalement pas de problèmes particuliers, nous avons simplement ajouter les fonctions sur les variables.

3. Administration Termes interdits

La première chose est de créer la partie administration pour cette liste de mots. Nous appellerons cette table "interdit". Nous allons utiliser simplement les notions vues au chapitre 14. Cette table ne reprend qu'un seul champ: interdit de type varchar(30).

3.1 Création de la table Interdit

La requête de création de table MYSQL reprend la commande CREATE TABLE [IF NOT EXIST] nom_table (definition-colonne, Index). Le champ interdit est une clé primaire, la valeur nulle est interdite.

<?php
require('../includes/start.php');

$requete="CREATE TABLE if not exists interdit (interdit varchar(30) primary key NOT NULL)";
$erreur=mysql_query($requete); 
$erreur1=mysql_error(); 
print($erreur."<br>"); 
print($erreur1); 
mysql_close(); 
?>

Nous aurions pu également utiliser les fichiers stop.php et start.php

3.2. Insertion des mots interdits.

Nous allons simplement créer un petit formulaire d'entrée comme exercice. Une version plus complète sera vue juste en-dessous

<form METHOD=\"POST\">
<p>Mot ou suite de mots interdits <input type="text" name="interdit" size="20"></p>

<p><input type="submit" value="Envoyer" name="go"></p>

</form>

Comme d'habitude, ce formulaire est auto-invocant. Le codage PHP complet de l'insertion devient:

<?php
$interdit =$_POST['interdit'];
$erreur_interdit = "";
//
if ($interdit == '')
{
$erreur_interdit = "La suite de mots interdits doit être renseignée<br>";
} else {
$interdit = ADDSLASHES($interdit);
require('../includes/start.php');
$requete="insert into interdit Set interdit='$interdit'";
$erreur=mysql_query($requete);
require('../includes/stop.php');
}
$form = "<table>
<form METHOD=\"POST\">
<p>Mot ou suite de mots interdits <input type=\"text\" name=\"interdit\" size=\"30\"><font color=\"#FF0000\">$erreur_interdit</font></p>
<p><input type=\"submit\" value=\"Insérer\" name=\"go\"></p>
</table>
</form>";
print($form);
?>

Comme cette partie fait partie de l'administration, elle sera insérée dans un sous-dossier (typiquement admin) protégé. Ceci nous conduit à faire quelques simplification. La première, il n'y a pas de tests pour vérifier si l'utilisateur utilise le bouton "Insérer". Le deuxième est lié au dossier includes qui devient require('../includes/stop.php'); Comme le champ interdit est une clé primaire, il n'est pas non plus nécessaire de tester si le mot existe déjà, il sera automatiquement supprimé de la liste par MySQL.

3.3. Afficher les mots interdits

Pour pouvoir vérifier les mots insérés, nous allons simplement afficher en liste les mots que nous avons insérés.

<?php
require('../includes/start.php');
$requete="select * from interdit";
$valeur=mysql_query($requete);
$tableau=array();
while ($tableau=mysql_fetch_array($valeur))

$interdit=STRIPSLASHES($tableau['interdit']);
print($interdit."<br>");
}
require('../includes/stop.php');
?>

Ce script ne fait qu'afficher les mots dans une liste, les uns après les autres. La partie suivante va les afficher dans un formulaire en liste déroulante.

3.4. Ajouter, modifier, supprimer les mots interdits.

Dans les 2 parties ci-dessus, nous avons découpé les fonctionnalités. Pourtant, le plus facile serait de rassembler toutes les fonctions. La modification ou la suppression d'un mot dans la liste utilise un formulaire pour rentrer le mot (ou la liste de mots) dans une liste déroulante qui va nous servir également à afficher les mots interdits.

Une deuxième liste de choix permet de supprimer, modifier ou insérer.

Une troisième zone de texte permet de modifier le choix sélectionner en 1 (cas modifier) ou d'insérer (cas Insérer).

Le plus facile reste d'afficher les valeurs déjà rentrer dans une liste déroulante. Ceci facilite la sélection. Commençons par créer la liste déroulante. Un formulaire standard est de type:

<form method=POST>

<p>Mots <select size=1 name=mot>

<option>liste déroulante</option>

<option>liste déroulante 2</option>

<option>...</option>

</select> 

<select name=action>

<option>Insérer</option><option>Modifier</option><option>Effacer</option>

</select>

nouvelle valeur: <input type=text name=nouveau size=30>

<input type=submit value=Envoyer name=go></p></form>";

Ceci affiche:

Mots   nouvelle valeur:

Ce type de développement a déjà été effectué pour les catégories. Dans le cadre de cette formation, nous allons le refaire. Décomposons le travail:

<?php
$mots_interdits=array();
$tableau=array();
// début du tests auto-invocant
if (isset($_POST['go']))
{
// variable reprenant les 3 paramètres du formulaire
$choix =$_POST['mot'];
$action=$_POST['action'];
$nouveau=ADDSLASHES($_POST['nouveau']);
// vérification du deuxième bouton
if ($action=="Effacer"){

// cas effacer, on efface le choix sélectionné dans la liste déroulante
require ('../includes/start.php');
$requete = "delete from interdit where interdit='$choix'";
$valeur = mysql_query($requete);
}elseif ($action=="Modifier"){

// Cas modifier: on remplace la valeur dans la liste déroulante par celle de la zone texte Nouvelle valeur
if ($_POST['nouveau']=="")
{

   // on teste si la valeur de remplacement est nulle. Dans ce cas aucune action
  Echo"Aucune valeur de remplacement sélectionnée, recommencez";
}else{

  // sinon on effectue le remplacement
  require('../includes/start.php');

  $requete="UPDATE interdit SET interdit='$nouveau' WHERE interdit='$choix'";
  $valeur=mysql_query($requete);
}

// fin de modifier
}elseif (($action=="Insérer")and($nouveau<>'')){

// insertion d'une nouvelle valeur si elle n'est pas nulle
require('../includes/start.php');
$requete="insert into interdit Set interdit='$nouveau'";
$erreur=mysql_query($requete);
}
}

// fin de la boucle, début de récupération des mots
require('../includes/start.php');
$requete="SELECT * FROM interdit";
$valeur=mysql_query($requete);
$i=1;
$ligne=0;

while ($tableau=mysql_fetch_array($valeur)){
// on récupère les données de la table
// print($ligne);
$mots_interdits[$i]=STRIPSLASHES($tableau['interdit']);
$i=$i+1;
}
require('../includes/stop.php');
// Affichage du formulaire
$ligne=$i;
$i=1;
$formulaire="<form method=\"POST\"><p>Mots <select size=\"1\" name=\"mot\">";
while ($i<$ligne){
$formulaire=$formulaire."<option>".$mots_interdits[$i]."</option>";
$i=$i+1;
}
$formulaire=$formulaire."</select> <select name=\"action\"><option>Insérer</option><option>Modifier</option><option>Effacer</option></select> nouvelle valeur: <input type=\"text\" name=\"nouveau\" size=\"30\"><input type=\"submit\" value=\"Envoyer\" name=\"go\"></p></form>";
print($formulaire);
?>

Quelques remarques, nous reprenons la liste des valeurs en fin de programme. Ceci permet à la liste d'être remise à jour à chaque fois. L'utilisation du bouton Envoyer est obligatoire

3. Tests et enregistrements des petites annonces avec mots interdits.

Nous pourrions simplement supprimer les annonces reprenant les mots interdits, mais nous allons les conserver. Nous avons 2 solutions:

La deuxième méthode est plus facile à mettre en oeuvre mais nécessite de revoir toute la programmation. De plus, elle permet d'afficher à quelques utilisateurs privilégiés également les tables non valides. Par contre, avec la première méthode. L'affichage, la suppression définitive, ... ne peut se faire que via le panneau d'administration.

Dans ce développement, nous allons créer une table équivalente que nous nommerons invalide. Voici le programme de création de la table. Il est parfaitement identique à celle de la table annonce (y compris avec les commandes ALTER pour rajouter les champs.

// création table contenu des annonces invalides

<?php

$requete="CREATE TABLE if not exists invalide (code int primary key NOT NULL auto_increment,titre varchar(120) not null,description blob not null,photo varchar(255), ville varchar(40),pays enum('Belgique','France','Luxembourg'),prix decimal(8,2) not null,dateinsertion date,telephone varchar(15),mail varchar(30),index(ville(10)))";
$erreur=mysql_query($requete);
$erreur1=mysql_error();
$requete="alter table invalide add uid_cat smallint(6) NOT NULL, add uid_util int(10) NOT NULL, add tel char(1)";
$erreur=mysql_query($requete);

?>

Cette solution permettra de reprendre les mêmes requêtes MySQL, en changeant uniquement le nom de la table.

3.1. Vérification de la présence des mots interdits dans un texte

Pour vérifier si un mot (ou une suite de mots) est présent dans un texte, nous utilisons:

EREG($variable1,$variable2) ne doit plus être utilisé à partir de PHP5.3.0

STRPOS($variable1,$vatiable2) recherche si la chaîne $variable2 est contenue dans $variable1, renvoie le nombre d'occurence

Cette fonction PHP a été utilisée comme exercice pour détecter un numéro de TVA belge. Dans notre cas, nous allons utiliser comme variable1 un ensemble de variables reprises dans une table. Ceci nécessite une boucle pour vérifier l'ensemble des valeurs interdites.

Dans la partie insertion des données du chapitre 19, aucune vérification n'est faite sur aucun champ (excepté pour la manipulation des photos). Nous allons simplement tester tous les champs avec les mots interdits. Dans la cas où un des mots de la liste est détecté, on va donner au paramètre (variable) $accepté la valeur"O". Si le mot est détecté dans un des champs, la valeur du paramètre devient "N". Au niveau de l'enregistrement, si le paramètre est OUI, il est enregistré dans la table "annonce". Dans le cas contraire, il est enregistré dans la table "invalide" préalablement créé. Charge à l'administrateur de supprimer cette annonce.

A la suite de cette partie du programme, nous allons insérer les différents tests

$titre = ADDSLASHES($_POST['titre']);
$erreur_titre = "";
$description =ADDSLASHES($_POST['description']);
...
$dateinsertion=date('Ymd');

Nous allons créer une fonction personnelle PHP comme vu au chapitre 4. Cette fonction demande en entrée un texte, en sortie, elle renvoie "O" si un des mot n'est pas inclus et "N" dans le cas contraire. Commençons par créer cette fonction personnelle. Un petit rappel, les fonctions personnelles sont de la forme:

  • function nom-fonction($argument1, $argument2) {
  • // code php
  • return $variable;
  • }

Les paramètres à envoyer à la fonction sont le textes de l'annonce tout simplement. Le paramètre de retour est simplement vrai ou faut. Décomposons le fonctionnement du programme par facilité. Commençons par un développement simple, sans nous occuper des mots interdits reprenant la table.

<?php
$texte="Cette phrase va être analysée pour détecter des mots interdits";
$correct=motinterdit($texte);
print($correct);


function motinterdit($texte){
$interdit="mots";
$correct="1";

if (STRPOS($texte,$interdit)===FALSE)
{
$correct="0";
}
return $correct;
}
?>

Dans ce petit programme, nous définissons le texte à tester au préalable via la variable $texte. La valeur $correct est déterminée par la fonction motinterdit. Celle-ci renvoie "1" si le mot est inclus dans $texte, "0" sinon. C'est justement ce que nous souhaitons. La seule difficulté va être de récupérer les mots à tester dans la table invalide et de faire le texte en boucle tant que tous les mots interdits ne sont pas testés.

Nous allons décomposer cette fonctions en 2: une partie pour reprendre la liste des mots sous forme de variable tableau (déjà faite) et une partie pour la détection. Cette méthode est logique puisque le tests va être fait sur plusieurs champs. Autant que la récupération se fasse une seule fois et les tests à chaque fois.

La partie récupération a déjà été développée.

require('../includes/start.php');
$requete="SELECT * FROM interdit";
$valeur=mysql_query($requete);
$i=1;
$ligne=0;

while ($tableau=mysql_fetch_array($valeur)){
// on récupère les données de la table
// print($ligne);
$mots_interdits[$i]=STRIPSLASHES($tableau['interdit']);
$i=$i+1;
}
require('../includes/stop.php');

Toute la difficulté va être d'envoyer et et de récupérer plusieurs variables via une fonction. Hors, si une fonction accepte plusieurs paramètres en entrée (avec un nombre fixé en plus), elle n'accepte pas de renvoyé plusieurs paramètres via la commande return. La solution passe par une matrice (une variable de type tableau). Essayons le petit programme ci-dessus.

<?php
$tableau1=array();
$tableau1[1]="rien";
$tableau1[2]="rien";
tests($tableau1);
// envoit tableau
$rien1=array();
$rien1=renvoit();
print("<br>".count($rien1)." valeurs récupérées de la fonction renvoit()");
function renvoit()
{
// cette fonction renvoie un tableau
$rien=array();
$rien[1]="1";
$rien[2]="2";
return $rien;
}
function tests($tableau)
// cette fonction reçoit une variable tableau et affiche le nombre de cellules.
{
print(count($tableau)." valeurs transférées à la fonction tests");
}
?>

Nous commençons par créer le tableau $tableau1 et nous l'envoyons dans la fonction tests. Celle ne fait que compter le nombre de cellule dans la variable transférée et afficher le nombre (count($tableau)). La ligne suivante définit la matrice $rien1 et récupère les données de la fonction renvoit(). Cette fonction ne fait finalement que de créer un tableau $rien à 2 entrées et le renvoi comme résultat de la fonction. C'est justement ce que nous voulons.

Dans le premier cas, nous souhaitons récupérer le nombre de mots interdits et les insérer dans une variable tableau (la fonction renvoit() dans l'exemple ci-dessus. Une fois les mots déterminés, nous devons renvoyer ces mots dans une fonction pour les tester (c'est la fonction tests() ci-dessus).

Le petit script en PHP ci-dessus récupère les variables de notre table MySQL via la fonction motsinterdits() et les affichent. Cette fonction doit être insérée dans le dossier include, ce qui explique que require('start.php'); ne tient pas compte du répertoire

<?php
$mots_interdits=array();
$mots_interdits=motsinterdits();
$ligne=count($mots_interdits);
print($ligne." lignes <br>");
$i=1;
while($i<=$ligne)
{
print($mots_interdits[$i]."<br>");
$i=$i+1;
}

function motsinterdits()
{
require('start.php');
$requete="SELECT * FROM interdit";
$valeur=mysql_query($requete);
$i=1;
while ($tableau=mysql_fetch_array($valeur)){
// on récupère les données de la table
$mots_interdits[$i]= STRIPSLASHES($tableau['interdit']);
$i=$i+1;
}
require('stop.php');
print("<br>termine <hr>");
return $mots_interdits;
}

La deuxième fonction va être de tester si un des mots existe dans une variable $texte via la fonction STRPOS() de l'exemple ci-dessus. Le petits programme ci dessus récupère les mots interdits via la fonction ci-dessus, assigne à $texte le texte à tester. Dans notre cas, nous lui avons défini la valeur "tests sur les mots interdits". Si la chaîne contient un des mots refusé, la fonction renvoie "N", sinon, "O" (valeurs correctes). Remarque si la liste contient une suite de mots, elle ne vérifie pas dans le désordre. Il faut donc mieux insérer dans la table des mots que des suites de mots.

<?php
$mots_interdits=array();
$mots_interdits=motsinterdits();
$ligne=count($mots_interdits);
print($ligne." lignes <br>");
$i=1;
while($i<=$ligne)
{
print($mots_interdits[$i]."<br>");
$i=$i+1;
}
$texte="tests sur les mots interdits";
$correct=inclusinterdits($texte,$mots_interdits);
print($correct);
function inclusinterdits($texte,$mots_interdits)
{
$i=1;
$ligne=count($mots_interdits);
$correct="O";
while ($i<=$ligne)
{
if (STRPOS($texte,$mots_interdits[$i])>0)
{
$correct="N";
}
$i=$i+1;
}
return $correct;
}

function motsinterdits()
{
require('start.php');
$requete="SELECT * FROM interdit";
$valeur=mysql_query($requete);
$i=1;
while ($tableau=mysql_fetch_array($valeur)){
// on récupère les données de la table
$mots_interdits[$i]=STRIPSLASHES($tableau['interdit']);
$i=$i+1;
}
require('stop.php');
print("<br>termine <hr>");
return $mots_interdits;
}

Il ne nous reste plus qu'à insérer ces 2 fonctions dans le fichiers fonctions.php inclus dans le dossier includes, appelée avec le début des procédures.

3.2. Correction de notre programme d'insertion de petites annonces.

Pour éviter que les spamsmeurs ne détectent trop la méthode, rien ne préviendra que le message est mis de coté. Tous les tests standards sont donc effectuer. Nous allons simplement effectuer les tests ci-dessus à la récupérations des données via la formulaire et modifier la requête d'insertion si la variable $correct="N" pour l'insérer dans l'autre table. Nous pourrons alors comme administrateur vérifier ces annonces, les supprimer, les modifier ou même les renvoyer vers la table standard après vérification.

Le programme d'insertion corrigé est accessible ici, en rouge, les lignes ajoutées.

Nous devons également modifier la fonction inclusinterdits (dans fonctions.php). La valeur $correct précédente lui est directement envoyée.

function inclusinterdits($texte,$mots_interdits,$correct)
{
$i=1;
$ligne=count($mots_interdits);
while ($i<=$ligne)
{
if (STROPS($texte,$mots_interdits[$i])>0)
{
$correct="N";
}
PRINT($correct.$mots_interdits[$i]);
$i=$i+1;
}
return $correct;
}
?>

La modification ne fait qu'insérer les 2 fonctions à la fin de la récupération par post des variables pour chaque valeur entrée par l'utilisateur. La valeur $correct est mise à "O" en début de déclaration et l'envoie également vers la fonction. A chaque appel de la fonction, elle renvoie la valeur $correct, égale à celle rentrée s'il n'y a pas de problème, N si un mot interdit est détecté.

4. Conclusion

Nous venons déjà de bloquer certaines rentrées. Le chapitre suivant va tester les annonces au niveau des liens inclus (une autre spécialité des webmasters indélicats). 

Une remarque des participants au cours: "De toute façon, l'utilisateur doit rentrer son adresse mail avant de poster une annonce". Effectivement, lorsque tout le développement sera fini, ce sera une des conditions pour poster une petite annonce. Malheureusement, ce n'est pas une sécurité. Si cette méthode permet de bloquer les logiciels automatiques, elle ne bloque pas le spams manuel (créer une adresse mail est relativement facile et par principe, je ne bloque pas les adresses mail gratuites). Et puis, un habitué peut toujours décidé de vendre un produit spécial sans but de spams.

A ce stade, je sépare en 2 parties. Le site dahut.be reprend la suite du développement sur cette page: Aide sur le développement petites annonces. YBET.be reprenant la suite de la formation PHP - MySQL

Sur le sujet

>23. Méthode de suivi des visiteurs
<21. Affichage des annonces

Mise en ligne: 09/11/2006