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.