Injection SQL
De nombreux d�veloppeurs web ne sont pas conscients des possibilit�s de manipulation des requ�tes SQL, et supposent que les requ�tes SQL sont des commandes s�res. Cela signifie qu'une requ�te SQL est capable de contourner les contr�les et v�rifications, comme les identifications, et parfois, les requ�tes SQL ont acc�s aux commandes d'administration.
L'injection SQL directe est une technique o� un pirate modifie une requ�te SQL existante pour afficher des donn�es cach�es, ou pour �craser des valeurs importantes, ou encore ex�cuter des commandes dangereuses pour la base. Cela se fait lorsque l'application prend les donn�es envoy�es par l'internaute, et l'utilise directement pour construire une requ�te SQL. Les exemples ci-dessous sont bas�s sur une histoire vraie, malheureusement.
Avec le manque de v�rification des donn�es de l'internaute et la connexion au serveur avec des droits de super utilisateur, le pirate peut cr�er des utilisateurs, et cr�er un autre super utilisateur.
Exemple #1 S�paration des r�sultats en pages, et cr�er des administrateurs (PostgreSQL et MySQL)
<?php
$offset = $argv[0]; // Attention, aucune validation!
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
// avec PostgreSQL
$result = pg_query($conn, $query);
// avec MySQL
$result = mysql_query($query);
?>
Un utilisateur normal clique sur les boutons 'suivant' et 'pr�c�dent', qui sont alors plac�s dans la variable $offset, encod�e dans l'URL. Le script s'attend � ce que la variable $offset soit alors un nombre d�cimal. Cependant, il est possible de modifier l'URL en ajoutant une nouvelle valeur, au format URL, comme ceci :
Exemple #2 Exemple d'injection SQL
<?php // cas de PostgreSQL 0; insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd) select 'crack', usesysid, 't','t','crack' from pg_shadow where usename='postgres'; -- // cas de MySQL 0; UPDATE user SET Password=PASSWORD('crack') WHERE user='root'; FLUSH PRIVILEGES; ?>
Si cela arrive, le script va cr�er un nouveau super utilisateur. Notez que la valeur 0; sert � terminer la requ�te originale et la terminer correctement.
Note: C'est une technique r�pandue que de forcer l'analyseur SQL � ignorer le reste de la requ�te, en utilisant les symboles -- pour mettre en commentaires.
Un moyen disponible pour acc�der aux mots de passe est de contourner la recherche de page. Ce que le pirate doit faire, c'est simplement voir si une variable du formulaire est utilis�e dans la requ�te, et si elle est mal g�r�e. Ces variables peuvent avoir �t� configur�es dans une page pr�c�dente pour �tre utilis�es dans les clauses WHERE, ORDER BY, LIMIT et OFFSET des requ�tes SELECT. Si votre base de donn�es supporte les commandes UNION, le pirate peut essayer d'ajouter une requ�te enti�re pour lister les mots de passe dans n'importe quelle table. Utiliser la technique des mots de passe chiffr�s est fortement recommand�.
Exemple #3 Liste d'articles ... et ajout de mot de passe
<?php
$query = "SELECT id, name, inserted, size FROM products
WHERE size = '$size'
ORDER BY $order LIMIT $limit, $offset;";
$result = odbc_exec($conn, $query);
?>
La partie statique de la requ�te, combin�e avec une autre requ�te SELECT, va r�v�ler les mots de passe :
Exemple #4 R�v�lation des mots de passe
<?php ' union select '1', concat(uname||'-'||passwd) as name, '1971-01-01', '0' from usertable; -- ?>
Si cette requ�te, exploitant les ' et -- est affect�e � une variable utilis�e dans $query, une injection SQL va se produire.
Les commandes UPDATE sont aussi sujettes � des attaques de votre base de donn�es. Ces requ�tes peuvent aussi introduire toute une nouvelle requ�te dans votre commande initiale. Mais en plus, le pirate peut jouer sur la commande SET. Dans ce cas, il doit conna�tre un peu votre base de donn�es. Cela peut se deviner en examinant les noms de variables dans les formulaires, ou simplement, en testant les cas les plus classiques. Il n'y a pas beaucoup de conventions de noms pour stocker des noms d'utilisateurs et des mots de passe.
Exemple #5 Modifier un mot de passe ... et gain de droits!
<?php
$query= "UPDATE usertable SET pwd='$pwd' WHERE uid='$uid';";
?>
Mais un internaute fourbe peut envoyer une valeur telle que ' or uid like '%admin%'; -- dans $uid pour modifier le mot de passe utilisateur, ou simplement, utiliser la variable $pwd avec la valeur "hehehe', admin='yes', trusted=100 " (avec l'espace final) pour obtenir des droits suppl�mentaires. La requ�te devient alors :
Exemple #6 Une requ�te et son injection
<?php
// $uid == ' or uid like'%admin%'; --
$query = "UPDATE usertable SET pwd='...' WHERE uid='' or uid like '%admin%'; --";
// $pwd == "hehehe', admin='yes', trusted=100 "
$query = "UPDATE usertable SET pwd='hehehe', admin='yes', trusted=100 WHERE ...;"
?>
C'est un exemple terrible d'acquisition de droits d'administrateur sur un serveur de base de donn�es.
Exemple #7 Attaque d'un serveur de bases de donn�es (MSSQL Server)
<?php
$query = "SELECT * FROM products WHERE id LIKE '%$prod%'";
$result = mssql_query($query);
?>
Si le pirate injecte la valeur a%' exec master..xp_cmdshell 'net user test testpass /ADD' -- dans la variable $prod, alors la requ�te $query devient :
Exemple #8 Attaque d'un serveur de base de donn�es (MSSQL Server) - 2
<?php
$query = "SELECT * FROM products
WHERE id LIKE '%a%'
exec master..xp_cmdshell 'net user test testpass /ADD'--";
$result = mssql_query($query);
?>
MSSQL Server ex�cute les requ�tes SQL en lot, y compris la commande d'ajout d'un nouvel utilisateur � la base de donn�es locale. Si cette application fonctionnait en tant que sa et que le service MSSQLSERVER disposait de niveau de droits suffisant, le pirate dispose d�sormais d'un compte avec acc�s au serveur.
Note: Certains des exemples ci-dessus sont sp�cifiques � certains serveurs de bases de donn�es. Cela n'emp�che pas des attaques similaires d'�tre possibles sur d'autres produits. Votre base de donn�es sera alors vuln�rable d'une autre mani�re.
Techniques de contournement
Vous pouvez pr�tendre que le pirate doit d'abord obtenir des informations sur le sch�ma de la base de donn�es, dans la plupart des cas d'injections. C'est vrai, mais vous ne saurez jamais comment ni quand ces informations auront filtr�, et si cela arrive, votre base de donn�es sera en grand danger. Si vous utilisez une base de donn�es Open Source, ou une base qui est du domaine public, ou encore un sch�ma qui appartient � un syst�me de gestion de contenu ou d'un forum, le pirate peut facilement se procurer une copie du code que vous utilisez. Cela peut �tre un risque potentiel si la base a �t� mal con�ue.
Ces attaques sont g�n�ralement bas�es sur l'exploitation de code qui n'est pas �crit de mani�re s�curitaire. N'ayez aucune confiance dans les donn�es qui proviennent de l'utilisateur, m�me si cela provient d'un menu d�roulant, d'un champ cach� ou d'un cookie. Le premier exemple montre comment une requ�te peut causer un d�sastre.
- Ne nous connectez jamais sur une base de donn�es en tant que super utilisateur ou propri�taire de la base. Utilisez toujours un utilisateur adapt�, avec des droits tr�s limit�s.
- V�rifiez que les donn�es ont bien le type attendu. PHP dispose d'un �ventail de fonction de validation large, depuis les plus simples, de la section Variables et la section Caract�res (e.g. is_numeric(), ctype_digit() respectivement) aux fonctions avanc�es de Expression rationnelle Perl.
-
Si l'application attend une entr�e num�rique, v�rifiez vos donn�es avec la fonction is_numeric(), ou bien modifiez automatiquement le type avec la fonction settype(), ou encore avec sprintf().
Exemple #9 Une navigation de fiches plus s�curitaire
<?php
settype($offset, 'integer');
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;";
// notez que %d dans la cha�ne de format : %s serait inutile
$query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;",
$offset);
?> - Mettez entre guillemets toutes les valeurs non num�riques qui sont pass�es � la base de donn�es avec la fonction sp�cifique � la base de donn�es d'�chappement de caract�res (e.g. mysql_real_escape_string(), sqlite_escape_string(), etc.). Si un m�canisme d'�chappement sp�cifique � une base de donn�es n'existe pas, les fonctions addslashes() et str_replace() peuvent �tre tr�s utiles (en fonction du type de la base de donn�es). Lisez le premier exemple. Comme le montre l'exemple, ajouter des guillemets � la partie statique de la requ�te n'est pas suffisant, rendant cette requ�te facile � pirater.
- N'affichez jamais d'informations sp�cifiques � la base, et notamment des informations concernant le sch�ma. Voyez aussi la section Rapport d'erreur et le chapitre Gestion des erreurs.
- Vous pouvez avoir des proc�dures stock�es et des curseurs pr�d�finis qui font que les utilisateurs n'ont pas un acc�s direct aux tables ou vues, mais cette solution a d'autres impacts.
� c�t� de ces conseils, il est recommand� d'enregistrer vos requ�tes, soit dans vos scripts, soit dans la base elle-m�me, si elle le supporte. �videmment, cet enregistrement ne sera pas capable d'emp�cher une attaque, mais vous permettra de retrouver la requ�te qui a faut�. L'historique n'est pas tr�s utile par lui-m�me, mais au niveau des informations qu'il contient. Plus vous avez de d�tails, mieux c'est.