Salut,
«Une injection sql malgré l'utilisation de mysql_real_escape_string ?
As tu un exemple en tête ?»Un exemple (a regarder avec une police a chasse fixe) :
mysql> DESCRIBE hotel;
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id_hotel | int(11) | NO | PRI | NULL | auto_increment |
| nb_etoiles | int(10) unsigned | YES | | NULL | |
| nom | varchar(25) | NO | | NULL | |
| ville | varchar(25) | NO | | NULL | |
+------------+------------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
<?php
$conf = array('mysql_host' => 'host',
'mysql_user' => 'user',
'mysql_pass' => 'password',
'mysql_db' => 'database');
if (empty($_GET['nb_etoiles']))
die('Aucun nombre d\'étoiles indiqué.');
mysql_connect($conf['mysql_host'], $conf['mysql_user'], $conf['mysql_pass']);
mysql_select_db($conf['mysql_db']);
$q = 'SELECT * FROM hotel WHERE nb_etoiles = ' . mysql_real_escape_string($_GET\
['nb_etoiles']);
$result = mysql_query($q) or die(mysql_error());
echo 'Nos choix d\'hotels :<br />';
while ($data = mysql_fetch_array($result))
{
echo '<p>';
echo 'Hotel : ', $data['nom'], '<br />';
echo 'Ville : ', $data['ville'], '<br />';
echo 'Nombre d\'étoiles : ', $data['nb_etoiles'], '<br />';
echo '</p>';
}
?>
?nb_etoiles=-42%20UNION%20SELECT%20NOW(),%20DATA_TYPE,%20TABLE_NAME,%20COLUMN_NAME%20FROM%20information_schema.COLUMNS%20WHERE%20TABLE_SCHEMA=DATABASE()
Ce que j'envoie ne contient aucun caractère échapé par mysql_real_escape_string. Ça fonctionne car on attend un int : il n'y a pas de quote a fermer pour injecter ce que l'on veux. Du coup, en utilisant les fonctions de MySQL on a tout ce qu'il nous faut pour injecter les divers chaînes dont on a besoin (merci CHAR). Un attaquat peux commencer a dumper toutes ta base comme je l'ai fait ici, puis recommencer pour aller chercher toutes les valeurs contenues dans tes tables. Autant te dire que si tu stocke des mots de passes en clair ton site est mort, si c'est du md5 ou sha1 ça va dépendre de sa complexité (un mot de passe simple est trouvé immédiatement avec les rainbow tables). Bien entendu, le mysql_error() sert a l'attaquant a ajuster sa requête pour arriver progressivement a en générer une correcte.
Solutions pour s'en protéger :
- Être un gros porc et mettre des quotes autours de la valeur de l'int dans la reqête sql. C'est super moche et bouffeur de ressources.
- Prendre le
intval() de la valeur. C'est cool mais ça ne prévient pas l'administrateur de ce qui se passe.
- Utiliser
is_numeric() pour lancer une exception. Loguer le timestamp, l'adresse ip du client et la requête complète peux être bien pour savoir ce qui se passe et éventuellement engager des poursuites si on a affaire a une tentative de piratage.
- Ne pas donner les erreurs détaillées a l'utilisateur permet de retarder voire décourager un attaquant.