[PHP] Sécurité : SQL Injection


[PHP] Sécurité : SQL Injection


Comment sécurisez son code contre les SQL Injections

- Kessako ?
===========
Cette methode/vulnerabilité consiste à fournir des données erronées (generalement via des formulaires) dans le but d'executer des requetes SQL non autorisés sur la base de données sur serveur hebergant le site "victime".
L'objectif est souvent celui de detruire/recuperer des données [censuré] ou d'éviter un système d'authentification.
elle est fort utilisé par les pirates, à cause de sa facilité et de l'anonymat et des pouvoirs qu'elle procure.

Plusieurs langages de scripting WEB sont vulnérable à ce type d'attaque.

- Et PHP ?
==========
Vu le danger que peut provoquer ce type d'attaque, et contrairement à d'autres langages de scripting web (ASP par exemple :D), les developpeurs de PHP on vite rajouté plein de fonctions et de parametres de configuration pour nous proteger contre les vils felons qui veuelent pirater notre cher serveur :p

A commencer par addslashes, en passant par magic_quotes_gpc dans php.ini, et finalement par les fonctionalités dependantes de la base de données.

- Par pratique :
============
comme je le dis toujours :
"Un p'tit bout de code vaut mieux qu'un long blabla ;)"

pour mieux comprendre voila donc un 'tit exemple de code.

<?php

include 'db-lib.inc.php';

$pseudo=$_POST['pseudo'];
$motdepasse=$_POST['motdepasse'];

$r=mysql_query("select pseudo from utilisateus where id='$pseudo' and password='$motdepasse'");

if (mysql_num_rows($r)!=0) {
echo "Success !";
// autre code [censuré] pour votre securité
} else {
echo "t'as encore oublié ton pseudo/pass !";
}

?>

sur le formulaire qui envoie vers ce fichier, je saisis:

pseudo: [Charly]
password: [blabla]


l'execution me donnera "t'as encore oublié ton pseudo/pass !" puisque je connais pas le pass de charly :D
la requete executé sera donc
{select pseudo from utilisateus where id='Charly' and password='blabla'}

rien d'etonnant, pour l'instant.
Maintenant supposant que l'admin du serveur est un noob et qu'il a des raisons valables pour mettre le magic_quotes_gpc=off dans le php.ini.

La je vais alors tenter
pseudo: [Nimportqui]
password: [tNul' or 'x'='x]

et la suprise (..ou pas pour les connaissance :p) j'aurai le message 'Success'.
Pourquoi ? il suffit de recontruire la requete :

{select pseudo from utilisateus where id='Nimportqui' and password='tNul' or 'x'='x'}

magique :D, en gros peu importe le login/mdp que j'aurai donné, ca marchera.
Certains me dirons, et alors ? c pas si dramatique que ca ;)
je reponderai que je mettrai dans password: [tNul'; drop database parano;]

et la ca serait le drame :D

- et la solution ?
=============

- magic_quotes_gpc: (fichier de conf php.ini)

ce parametre est mis à on par default, c'est pour ca d'ailleurs que les attaques SQL injection sont moins frequentes en PHP, cependant il presente pas une vrai solution "propre". Et surtout il necessite d'avoir un
accès previligié au serveur (pour pouvoir changer le php.ini).
Le soucis principale, est surtout, qu'une fois activé, on retrouve tous les données recus (POST, GET et COOKIE) addslashés (en gros un slash avant tout ' ou ") :p, ce qui peut etre tres genant dans certaines cas. surtout si on veut stocker les données tel qu'ils sont saisies par l'utilisateur dans un fichier/ou base données.

- addslashes: (fonction)
elle se presente comme une alternatif, quand magic_quotes_gpc n'est pas activé. cependant elle est pas tres fiable, car elle ne tiens pas en compte des autres caracteres spéciaux qui peuvent permettre des SQL Injection dans certains base de données.

- *_escape_string : fonctions
La meilleur pratique. Ca permet de n'ajouter de slash que lorsque les données sont destinés à la base.
Qui dit base de données, dit donc une fonction codé maison, ou une fonction existante selon la base de données utilisé.

Dans notre exemple, la base est du mysql. Un 'tit coup de RTFM (Read The *ucking Manuel) et vous retrouverez la fonction mysql_real_escape_string.

Cependant il y a un 'tit soucis. qu'est ce qu'il se passera si on a la magic-quote_gpc activé ? et ben il faudra soit la désactiver, sauf rajouter un bout de code pour enlever les slashes

si on prend l'exemple de notre code ca poura donner :
if (get_magic_quotes_gpc()) {
$_POST['pseudo']=stripslashes($_POST['pseudo']);
$_POST['motdepasse']=stripslashes($_POST['motdepasse']);
}

$pseudo=mysql_real_escape_string($_POST['pseudo']);
$motdepasse=mysql_real_escape_string($_POST['motdepasse']);


certe c'est tres lourd de faire ca à chaque fois. On cherchera mieux.
un 'tit coup d'oeil au manuel php.net/mysql_real_escape_string
et vous trouverez une fonction trop sympa, que vous pourrez integrer dans votre librarie de base de donnée (ici 'db-lib.inc.php'). En plus votre requete ressemble fortement aux requetes DB de Perl ;).
Plusieurs libraires/couches DB, offrent ce genre de methode (Pear::DB par ex;)

la fonction en question est :

function db_query($query) {
$args = func_get_args(); // Recupere tous les arguements dans un tableau
$query = array_shift($args); // retire la requete du tableau
$query = str_replace("?", "%s", $query); // remplate ? par %s
if (get_magic_quotes_gpc()) // enleve les slashes si magic_quotes_gpc est activé
$args = array_map('stripslashes', $args);
$args = array_map('mysql_real_escape_string', $args); // echape les caracteres speciaux
array_unshift($args,$query); // remet la requete dans le table
$query = call_user_func_array('sprintf',$args); // remplace les %s par les variables correponants
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
return $result;
}

le final code deviendra donc :

include 'db-lib.inc.php';

$r=db_query("select pseudo from utilisateus where id='?' and password='?'",$_POST['pseudo'],$_POST['motdepasse']);

if (mysql_num_rows($r)!=0) {
echo "Success !";
// autre code [censuré] pour votre securité
} else {
echo "casse toi, espece de felon :D";
}

voila ;) c'est tout pour le SQL Injection, je vous donne rendez-vous au prochain artice "Securité" ;)

A vos commentaires.

Happy Hacking ;)

Poster un commentaire:

Nom/Name
Comment.

#.$name (02 Dec 2006 - 07:23)

Very nice site!
#.Bill (10 Nov 2006 - 20:25)

Hi all
#.mrrgmxa (07 Nov 2006 - 14:21)

cool site! i love is it.
#.aissam (01 Jun 2006 - 01:12)


il y a de quoi avoir peur ;)

en fait, suivant la base de donnée, on peut reussir à faire des SQL Injection, en utilisant par exemple les autres caracteres speciaux (autre que ' et ";).
une autre technique consiste à exploiter le charset utilisé par la base de donnée:
en gros on saisit une chaine (par exemple [x' or pseudo=pseudo]), de facon a ce que le slash ajouté \ (code hexa 5C) forme avec le caractere precedant le ' (ici: x ,code hexa xYY) un caractere valable (caractere sur 2 bytes : xYY5C que je vais appeler Z) pour la base de donnée.
Au niveau du SGBD ca revien à executer la requete

{select pseudo from utilisateus where id='Nimportqui' and password='Z' or pseudo=pseudo}

dans notre exemple, et contrairement à addslashes, mysql_real_escape_string prend en consideration le charset utilisé pas la base ;)
#. S533 (01 Jun 2006 - 01:12)

addslashes () n'est une une façon sur de sécurité nos scripts ?
Tu me fais peur, la...

Pourrais tu me montrer comment l'on peut passer cette protection ?

Merci d'avance.
 1
5 commentaires