Quand il s'agit de requêtes de base de données, essayez toujours d'utiliser des requêtes paramétrées préparées. Les bibliothèques mysqli
et PDO
prennent en charge cela. C'est infiniment plus sûr que d'utiliser des fonctions d'échappement telles que mysql_real_escape_string
.
Oui, mysql_real_escape_string
c'est en fait juste une fonction d'échappement de chaîne. Ce n'est pas une solution miracle. Tout ce qu'il fera est d'échapper aux caractères dangereux afin qu'ils puissent être utilisés en toute sécurité dans une seule chaîne de requête. Cependant, si vous ne nettoyez pas vos entrées au préalable, vous serez vulnérable à certains vecteurs d'attaque.
Imaginez le SQL suivant:
$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']);
Vous devriez être en mesure de voir que cela est vulnérable à l'exploitation.
Imaginez que le id
paramètre contienne le vecteur d'attaque commun:
1 OR 1=1
Il n'y a pas de caractères risqués à encoder, il passera donc directement à travers le filtre qui s'échappe. En nous quittant:
SELECT fields FROM table WHERE id= 1 OR 1=1
Ce qui est un joli vecteur d'injection SQL et permettrait à l'attaquant de renvoyer toutes les lignes. Ou
1 or is_admin=1 order by id limit 1
qui produit
SELECT fields FROM table WHERE id=1 or is_admin=1 order by id limit 1
Ce qui permet à l'attaquant de renvoyer les détails du premier administrateur dans cet exemple complètement fictif.
Bien que ces fonctions soient utiles, elles doivent être utilisées avec précaution. Vous devez vous assurer que toutes les entrées Web sont validées dans une certaine mesure. Dans ce cas, nous voyons que nous pouvons être exploités car nous n'avons pas vérifié qu'une variable que nous utilisions comme nombre, était en fait numérique. En PHP, vous devriez largement utiliser un ensemble de fonctions pour vérifier que les entrées sont des entiers, des flottants, des caractères alphanumériques, etc. Mais quand il s'agit de SQL, faites surtout attention à la valeur de l'instruction préparée. Le code ci-dessus aurait été sécurisé s'il s'agissait d'une instruction préparée, car les fonctions de base de données auraient su que ce 1 OR 1=1
n'est pas un littéral valide.
Quant à htmlspecialchars()
. C'est un champ de mines en soi.
Il y a un vrai problème en PHP en ce qu'il a toute une sélection de différentes fonctions d'échappement liées au HTML, et aucune indication claire sur exactement quelles fonctions font quoi.
Premièrement, si vous êtes à l'intérieur d'une balise HTML, vous avez de vrais problèmes. Regarder
echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';
Nous sommes déjà dans une balise HTML, nous n'avons donc pas besoin de <ou> de faire quoi que ce soit de dangereux. Notre vecteur d'attaque pourrait êtrejavascript:alert(document.cookie)
Le HTML résultant ressemble maintenant à
<img src= "javascript:alert(document.cookie)" />
L'attaque passe directement.
Ça s'empire. Pourquoi? car htmlspecialchars
(lorsqu'il est appelé de cette façon) n'encode que les guillemets doubles et non les simples. Donc si nous avions
echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";
Notre attaquant maléfique peut désormais injecter de tout nouveaux paramètres
pic.png' onclick='location.href=xxx' onmouseover='...
nous donne
<img src='pic.png' onclick='location.href=xxx' onmouseover='...' />
Dans ces cas, il n'y a pas de solution miracle, il vous suffit de santiser vous-même l'entrée. Si vous essayez de filtrer les mauvais caractères, vous échouerez sûrement. Adoptez une approche de liste blanche et ne laissez passer que les caractères qui sont bons. Regardez la feuille de triche XSS pour des exemples sur la diversité des vecteurs
Même si vous utilisez en htmlspecialchars($string)
dehors des balises HTML, vous êtes toujours vulnérable aux vecteurs d'attaque de jeux de caractères multi-octets.
Le plus efficace que vous puissiez être est d'utiliser la combinaison de mb_convert_encoding et htmlentities comme suit.
$str = mb_convert_encoding($str, 'UTF-8', 'UTF-8');
$str = htmlentities($str, ENT_QUOTES, 'UTF-8');
Même cela laisse IE6 vulnérable, en raison de la façon dont il gère UTF. Cependant, vous pouvez revenir à un encodage plus limité, tel que ISO-8859-1, jusqu'à ce que l'utilisation d'IE6 diminue.
Pour une étude plus approfondie des problèmes multi-octets, voir https://stackoverflow.com/a/12118602/1820