Un peu d'histoire
il y'a pas si longtemps voici le genre de question qui se succédèrent sur
les forum d'aide PHP.
Je fût même un de ceux là, qui pourtant, après une très brève recherche avec
l'ami de tous " Google " auraient trouvé pourquoi ca bug
< Mon site fonctionnait bien avant et depuis que j'ai changer d'hébergeur
j'ai que des message d'erreur ? >
< Mon site fonctionne en local, mais une fois mit en ligne j'ai plein de
message d'erreur ? >
< Bordel ! c'est quoi toutes ces erreur qui apparaissent d'un coup ? >
< Hier ca fonctionnait, pourquoi aujourd'hui c'est la cata ?! >
et j'en passe des bien pires, toutes ces questions auquel n'importe quel
débutant aurait demandé une réponse sont dûe à une seul chose, une seul
ligne dans un php.ini
register_globals = 0 ( OFF dans certains cas )
< Qu'est-ce que ca change ? >
Ca change pleins de choses, voyons un petit exemple simple, un formulaire
demandant simplement un nom et un prénom
[i]Nous passerons la balise <html><body><form>etc.., si vous en êtes au
point des register_globals c'est que vous connaissez ce language appelé
HTML, et les structures PHP à respectées[/i]
Code :
Votre nom : <input type="text" name="nom">
Votre prénom : <input type="text" name="prenom">
Avec register_globals activer nous pouvions récupérer notre formulaire avec
des variables qui était crée portant le " name " du champ
Donc sur la page suivante on pouvait, pour afficher notre nom et notre
prénom, que nous avions introduit dans le formulaire, faire ceci :
Code :
echo "Votre nom : " . $nom . "<br>Votre prénom : " . $prenom .
"<br><br>Merci de votre visite ;)";
< C'est plus simple pourquoi avoir désactiver register_globals ?, sa
simplifie le truc pourtant >
Oui, c'est vrai.
Ca simplifie, mais c'est beaucoup moins sécuriser, les changements que celà
apporte c'est une correction de failles sécuritaires
Des problèmes d'injection SQL, oui oui, il était possible de " contrôler "
votre base de données, indépendament de votre volonté, mais directement lier
à votre méthode de codage.
Le " cross-site scripting " , heuuu ok j'explique
Lorsque l'on remplissait un formulaire on pouvait très bien introduire du
contenu HTML et JAVASCRIPT ou inversément si vous voulez, et lorsque ce
formulaire était envoyé, ce même code ce retrouvait interpreté par le
navigateur et pouvait provoqué des comportement tout à fait différent de cu
que vous avez coder, il y'a d'autres raisons encore, mais je ne les émunère
pas, sinon j'écrit un bouquin ;)
La réponse ce résume donc à " un correctif de sécurité ", remercions les de
viser la sécurité avant la simplicité
Maintenant vous devez avoir compris à quoi sert register_globals, il ne vous
est plus possible de récupérer des variables de type $_GET['var'],
$_POST['var'], $_COOKIE['var'], $_SERVER['var'], $_ENV['var'],
$_REQUEST['var'], $_SESSION['var'] en les appelant directement par $var,
leur contenu n'est plus automatiquement échapé par des anti-slashes ( \ ).
Heureusement il y'a un remède ;)
Ce qu'il faut faire
Il faut partir du principe que toute variable ( contenu ) envoyée par
l'utilisateur, est potentiellement un risque et peu à tout moment crée un
comportement inatendu de notre script, à tout moment un " hacker " peu
profité d'une failles de sécurité due à un manque d'attention et de
vérification.
Ceci concerne tous vos formulaire, vos applications cookie, vos upload de
fichier et même vos URL si vous ne le saviez pas pas déjà
Il est donc très important de faire un maximum de vérification lors du
traitement de ces données " utilisateur ", de leur sauvegarde et de leur
affichage.
Il existe un " PROPRE " au codage, je veux dire que si c'est pour coder,
autant coder proprement et clairement, mettre des commentaires c'est très
utile pour s'y retrouver et même débugger une application.
Maintenant vous êtes obliger de passer par des variables définies, ces
variables sont en faite des tableaux associatif $_POST['var'], $_GET['var']
etc...
Mais leur valeur n'est pas forcément échappées par des anti-slashes, je vous
l'ai dit tout est potentiellement un risque !
Voici donc une solution à :
< Comment échappé automatiquement mes contenus " utilisateur " avec des
anti-slashes ? >
< Comment empêcher les attaque de type : INJECTION SQL ? >
< Comment récupér mes variable simplifiés ( $nom au lieu de $_POST['nom'] )
? >
Pour ce faire nous allons crée un nouveau fichier que nous allons appelé "
rg_parse.php "
On y introduit déjà la fonction nous retournant certains type d'erreur
Code :
<?PHP
// On commence par demander au serveur de nous renvoyer certaines erreurs
lors de
// l'exécution, dont voici les définitions :
// E_ERROR : erreur d'exécution de script
// E_WARNING : erreur d'exécution de script
// E_PARSE : erreur d'exécution de script
// E_NOTICE : Rapporter les E_NOTICE peut nous aider à améliorer notre
scripts
// (variables non initialisées, variables mal orthographiées..)
error_reporting (E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
Remarquez que j'ai mit des commentaires explicite et que je commence
toujours mon fichier par " <?PHP " ceratin préfèreront " <? "
qui est aussi correct mais rappelez vous " PROPRE "
[b]< Comment échappé automatiquement mes contenus " utilisateur " avec des
anti-slashes ? >[/b]
Un petit bout de code sur nos tableaux associatifs $_POST['var'] etc..., et
le tout toujours avec des commentaires
Code :
// SI get_magic_quotes_gpc n'existe pas ( est désactiver ), dans le cas
contraire
// les données ce verront automatiquement échapper par des antislashes ( \ )
if (!get_magic_quotes_gpc())
{
// Nous vérifions si $_GET est un tableau
// SI $_GET est un tableau
if (is_array($_GET))
{
// C'est bien un tableau, $_GET est un tableau associatif contenant
pour chaque array
// ( ligne du tableau ) une clé et une valeur
// each($_GET) = nous retourne chaque clé / valeur du tableau
associatif $_GET
// On fait une boucle sur notre tableau $_GET pour chaque clé /
valeur, en assignant
// la clé à la variable $cle et la valeur à la variable $val
while (list($cle, $val) = each($_GET))
{
// On vérifie si la clé en cours n'est pas elle même un tableau,
ceci est possible
// dans le cas d'un tableau multi-dimensionnel
// SI $_GET[$cle] est un tableau
if (is_array($_GET[$cle]))
{
// C'est bien un tableau, $_GET[$cle] est un tableau
associatif contenant pour chaque
// array ( ligne du tableau ) une clé et une valeur
// each($_GET[$cle]) = nous retourne chaque clé / valeur de
notre tableau associatif
// On fait une boucle sur notre tableau pour chaque clé /
valeur, en assignant
// la clé à la variable $cle2 et la valeur à la variable
$val2
while (list($cle2, $val2) = each($_GET[$cle]))
{
// Nous redéfinissons la valeur en cours en l'échappant
avec des antislashes ( \ )
$_GET[$cle][$cle2] = addslashes($val2);
}
// On remet le pointeur interne de tableau au début
@reset($_GET[$cle]);
}
// SINON ce n'est pas un tableau
else
{
// Nous pouvons directement redéfinir la valeur en cours en
l'échappant avec des
// antislashes ( \ )
$_GET[$cle] = addslashes($val);
}
}
// On remet le pointeur interne de tableau au début
@reset($_GET);
}
// Vous pouvez recommencer ce script qui est valable pour les tableaux
associatif $_GET, $_POST et $_COOKIE ( d'ou magic_quotes_gpc, get - post-
cookie )
// temps qu'il reste dans cette condition
}
Maintenant toutes nos variables " utilisateur " sont automatiquement échappé
par des anti-slashes, grâce à addslashes() ha ben ca facilite quand
même grandement les choses, de cette manière vous avez en même temps limiter
un peut plus les injections SQL ( mais bon faut pas ce leurer y'a toujours
moyen )
< Comment empêcher les attaque de type : INJECTION SQL ? >
Par la même occasion il sera ANTI-XSS/CSS ;), je vous laisse lire le code
tout est dit dans les commentaires du code
Code :
// strtolower : retourne string , après avoir converti tous les caractères
// alphabétique en minuscules
// !! Notez que la notion d'alphabétique est déterminée par la configuration
de
// !! localisation. Cela signifie que pour la configuration par défaut "C",
les
// !! caractères tels que les voyelles accentuées (comme é, è ou à) ne
seront pas convertis
// rawurldecode : retourne la chaîne str dont les séquences de caractères
%xy, avec
// xy deux valeurs hexadécimales, auront été
remplacées par le
// caractère ASCII correspondant.
// $_SERVER[] contient de nombreux renseignements sur le serveur ou la page
affichée.
// ['QUERY_STRING'] : QUERY_STRING est somme toute très pratique, elle
permet
// d'extraire la chaîne de requête de l'url
de la page en cours.
// Exemple : Vous appelez la page
http://www.domaine.com/index.php?partie=News
// $QUERY_STRING retournera simplement tout ce qui se situe après le point
// d'interrogation. Ici : partie=News
$query_str = strtolower(rawurldecode($_SERVER['QUERY_STRING']));
// On crée un tableau php contenant des éléments interdit
// Tiens qu'a vous d'en ajouter d'autres
$str_no = array(" union ",
"/*",
"*/union/*",
"+union+",
"load_file",
"outfile",
"document.cookie",
"onmouse",
"<script",
"<iframe",
"<applet",
"<meta",
"<style",
"<form",
"<img",
"<body",
"<link");
// foreach : C'est un moyen simple de passer en revue un tableau
// On passe donc en revue le tableau $str_no .
// A chaque itération, la valeur de l'élément courant est assignée à
$str_val et le
// pointeur interne de tableau est avancé d'un élément ( ce qui fait qu'à la
// prochaine itération, on accédera à l'élément suivant ).
foreach ($str_no as $str_val)
{
// strpos : retourne la position numérique de la première occurence de
$str_value
// dans la chaîne de caractères $query_str .
// En clair : si dans l'url il existe un élément ( valeur de notre
tableau php
// $str_no ) on redirige vers une page d'erreur
if (strpos($query_str, $str_val)) die("<br /><br /><br /><div
style=\"text-align: center;\"><big>Mais ma parole, t'a essayer de faire quoi
là ?</big></div>");
}
// unset() détruit les variables $str_queryurl, $str_no, $str_value que nous
avions
// crée ci-dessus
// !! Notez que unset() n'est plus une véritable fonction : c'est une
structure du
// !! langage, ce qui fait qu'elle ne retourne pas de valeur
// !! Lire la valeur retournée par unset() (dans une variable, par exemple),
retourne
// une erreur d'analyse
unset($query_str, $str_no, $str_val);
Ainsi tous ce qui sera passer par l'url contenant un élément interdit
arretera l'exécution du script ( die(...) ) pour afficher un message
d'erreur
Bon attaquons enfin la dernière question
[b]< Comment récupér mes variable simplifiés ( $nom au lieu de $_POST['nom']
) ? >[/b]
Alors là ca va assez vite
Code :
// Cette fonction va créer une variable pour chaque clé du tableau
associatif $_POST
// exemple : " $_POST['nom'] " deviendra " $nom "
// EXTR_SKIP : Lors d'une collision, ne pas réécrire la variable existante
extract($_POST, EXTR_SKIP);
clair, net et simple
N'oublions pas maintenant de refermer notre déclaration PHP
Code :
?>
Enregistré le fichier, maintenant il ne vous reste plus qu'a ajouter une
ligne dans vos fichier ( ou sur index.php dans le ca d'un site moduler )
Code :
// Si le fichier existe ( toujours vérifier je ne le dirais jamais trop )
if(is_file("rg_parse.php"))
{
// Alors on l'inclus, je préfère require à include, si le fichier ne
peux pas être inclus on aura
// un arret de l'exécution du script avec un beau " warning ",
contrairement à include qui
// affichera simplement une erreur sans stoper l'exécution du script
require("rg_parse.php");
}
Voilà félicitation vous avez terminé ;)
Maintenant n'oubliez pas que dès qu'il y'a moyen vous devez échapper avec
des anti-slashes vos variables, même avec la présence de ce script
rappelez-vous que nous avons mit [b]si magic_quotes_gpc n'est pas
activer[/b], il peux toujours y avoir une faille quelque part, si ce n'est
pas maintenant elle viendra, les hacker ca évolue vite
Les règles seront donc :
[b]échapper avec des anti-slashes lors de la réception pour enregistrement
dans une base de données[/b]
Code :
$nom = addslashes($nom);
[b]Remplacer les caractère HTML par leur équivalence ASCII exemple le
symbole < sera remplacé par <[/b]
Code :
$nom = htmlentities($nom);
Vous obtenez ainsi une valeur correct dans vos requêtes et l'enregistrement
ce fait sans problème
Mais lorsque vous voudrez afficher cette valeur il vous faudra les retirer
ces anti-sashes.
Rassurez-vous il existe aussi une solution pour ca
Code :
$nom = stripslashes($nom);
echo "Bonjour " . $nom . "<br>Bienvenue sur .....";
Voilà qui donne le " gong " à cet article, n'oubliez pas " PROPRE " c'est
plus important que ca peu le paraître, grâce à mes commentaires vous avez
compris la plupart des fonctions utiliser dans cet article, vous pouvez
aussi en venir discuter dans le forum ;)