Aller au menu - Aller au contenu

Icône Éviter les injections SQL

Mise à jour : 22/08/2008
396 visites depuis 7 jours, classé 272/786
Les injections SQL sont parmi les failles les plus répandues et les plus dangereuses en PHP. Ce tutoriel va vous expliquer clairement le principe des injections SQL, et la façon de les éviter une fois pour toutes.
Les risques que représente ce genre de faille sont énormes, spécialement si votre site utilise un espace membre, ou une zone d'administration, qui utilise une base de données MySQL (très courant).
Pour lire ce tuto, des bases en PHP / MySQL sont requises. Si vous avez lu le cours de M@teo21 sur le PHP, vous n'aurez aucun problème à suivre. ;)
Sommaire du tutoriel :
Icône du chapitre

Présentation du problème

Il y a deux types d'injections SQL :
  • l'injection dans les variables qui contiennent des chaînes de caractères ;
  • l'injection dans les variables numériques.


Ce sont deux types bien différents et pour les éviter, il faudra agir différemment pour chacun d'eux.

Les variables contenant des chaînes de caractères



Imaginons un script PHP qui va chercher l'âge d'un membre en fonction de son pseudo. Ce pseudo est passé d'une page à l'autre par l'intermédiaire de l'URL (par $_GET quoi :p ). Ce script devrait ressembler à ça :

Code : PHP
1
2
3
4
5
<?php
...
$pseudo = $_GET['pseudo'];
$requete = mysql_query("SELECT age FROM membres WHERE pseudo='$pseudo'");
...


Eh bien tenez-vous bien, ce script est une grosse faille d'injection SQL. Il suffira à un méchant garçon de mettre à la place du pseudo dans l'URL une requête de ce genre :

Code : SQL
1
' UNION SELECT password FROM membres WHERE id=1


C'est pour arriver à afficher (c'est juste un exemple) par exemple le mot de passe du membre avec l'id 1. Je n'expliquerai pas en détail l'exploitation, de peur que quelqu'un de pas gentil puisse se promener dans les parages. Voilà, passons donc à la sécurisation. :)

Sécurisation

Pour sécuriser ce type d'injection, rien de plus simple. Vous utiliserez la fonction mysql_real_escape_string().
Euh... Elle fait quoi, cette fonction ?

Cette fonction ajoute le caractère "\" aux caractères suivants :
Code : Autre
1
NULL, \x00, \n, \r, \, ', " et \x1a

Et ça sert à quoi ?

Comme vous avez dû le remarquer, dans l'injection précédente, le pirate utilise le caractère quote (pour fermer les ' qui entourent $pseudo) : si on l'empêche de faire ça, le méchant garçon n'aura qu'à aller voir ailleurs.
Ce qui signifie que si on applique un mysql_real_escape_string() à la variable pseudo comme ceci...

Code : PHP
1
2
3
4
5
<?php
...
$pseudo = mysql_real_escape_string($_GET['pseudo']);
$requete = mysql_query("SELECT age FROM membres WHERE pseudo='$pseudo'");
...

La requête est entièrement sécurisée.

Explication


L'injection du pirate est pour rappeler :
Code : SQL
1
' UNION SELECT password FROM membres WHERE id=1

Eh bien si on applique mysql_real_escape_string() à la variable $pseudo utilisée dans la requête, voilà ce que deviendra l'injection :
Code : SQL
1
\' UNION SELECT password FROM membres WHERE id=1

Ce qui signifie que l'on ne sortira même pas des quotes entourant $pseudo dans la requête puisque le \ a été ajouté.

Il existe une autre fonction quelque peu similaire à mysql_real_escape_string(), c'est Addslashes(), pourquoi ne pas l'avoir utilisée ? Eh bien très récemment, une faille de sécurité a été découverte sur cette fonction si elle est utilisée sur une installation PHP 4.3.9 avec les magic_quotes_gpc activés.


Les variables numériques



Ce genre d'injection est moins connu que le précédent, ce qui le rend plus fréquent, et on commence comme tout à l'heure avec un exemple. Cette fois, on affiche l'âge d'un membre en fonction de son id, et on fait passer cette dernière par un formulaire ($_POST) pour changer :D :

Code : PHP
1
2
3
4
5
<?php
... /* Le formulaire */ ...
$id = $_POST['id'];
$requete = mysql_query("SELECT age FROM membres WHERE id=$id");
...


mysql_real_escape_string() ne servirait à rien ici puisque si un pirate veut injecter du code SQL, il n'aura pas besoin d'utiliser les quotes, puisque la variable $id n'est pas entourée de quotes. Exemple simple d'exploitation :

Code : SQL
1
2 UNION SELECT password FROM membres WHERE id=1


Cette injection fait exactement la même chose que la précédente, sauf que là, pour l'éviter, il y a deux solutions :
  • changer le contenu de la variable pour qu'elle ne contienne que des nombres ;
  • vérifier si la variable contient réellement un chiffre avant de l'utiliser dans une requête.


Méthode 1



Nous allons utiliser une fonction qui a été abordée par M@teo_21 dans son cours PHP, intval() : cette fonction retourne quelque soit le contenu d'une variable sa valeur numérique. Par exemple :
Code : PHP
1
2
3
<?php
$variable = '1e10';  // $variable vaut '1e10'
$valeur_numerique = intval($variable); // $valeur_numerique vaut 1


Revenons maintenant à nos moutons :
Code : PHP
1
2
3
4
5
6
<?php
... /* Le formulaire */ ...
$id = intval($_POST['id']);
$requete = mysql_query("SELECT age FROM membres WHERE id=$id");
}
...

Voilà : vous pouvez vous arrêter là et c'est amplement suffisant, mais je vous recommande de continuer pour connaître l'autre méthode, sinon vous auriez l'air bête si vous trouviez cette méthode sur un code qui n'est pas le vôtre sans la comprendre.

Méthode 2



Là, on va utiliser une fonction qui retourne TRUE lorsqu'une variable ne contient que des nombres et FALSE si ce n'est pas le cas : cette fonction est is_numeric(), on va l'utiliser dans une condition qui vérifie si is_numeric() retourne bien TRUE.

Code : PHP
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<?php
$id = $_POST['id'];
if (is_numeric($id))
{
$requete = mysql_query("SELECT age FROM membres WHERE id=$id");
}
else
{
echo "Qu'est-ce que tu fais, petit malin ? ;)";
}


Quelle est la meilleure fonction, entre intval() et is_numeric() ?

Eh bien je vais dire que puisqu'elles sont toutes les deux aussi efficaces, elles sont égales.
Mais je préfère intval() puisqu'avec is_numeric() on écrit plus de code, et si la variable ne contient pas que des nombres, la requête est annulée (en principe, mais bien entendu, vous pouvez exécuter la même requête en choisissant une valeur par défaut pour la variable utilisée).
Eh bien voilà ! Vous savez tout à propos de la sécurisation de vos requêtes.
Si vous appliquez ces méthodes, il n'y a absolument aucun risque d'avoir une faille de type injection SQL sur son site web (ou script PHP).

Lien :
La faille sur Addslashes() (en)

Partager

59 commentaires pour "Éviter les injections SQL"
Note moyenne : 3.26 / 4 (23 votes)
Pseudo Commentaire
Hors ligne ilex # Posté le 28/11/2010 à 02:02:11
au lion, la part du lion
Avatar

Avis : Mitigé

Salut pas mal comme tuto mais je n'apprends rien de nouveau :p
Ce serait bien s'il y'avait un tuto complet pour les failles de tout types (JS également ..) :p

Merci à tout les ZerOs ! :) -



 
Hors ligne cerise13 # Posté le 10/02/2011 à 23:07:00

Salut,

Ce tuto m'a aidé a comprendre plusieurs chose, même si d autre trouve qu il est pas complet.

Merci
Hors ligne RyDroid # Posté le 15/04/2011 à 12:57:34
rayquaza devient RyDroid
Avatar

Avis : Bon

Ville : Reims
Pays : France métropolitaine

Bon tuto.
Il serait bien d'expliquer comme faire avec mysqli_* et PDO.
 
Hors ligne zulin # Posté le 15/04/2011 à 15:55:08
Avatar

Salut

Tu devrait peut être faire un tuto sur le sécurité sur le pdo en complément du tuto PHP/Mysql de Mateo21.
Hors ligne mickaelx4 # Posté le 07/11/2011 à 23:25:13
Avatar

zulin à raison c'était justement la raison de ma venu ^^

http://Mangazone.xoo.it/
Le forum francophone de tous les mangas !!!
 

Voir tous les commentaires
Ce tutoriel a été corrigé par les zCorrecteurs.