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
$result = "SELECT fields FROM table WHERE id = '".mysql_real_escape_string($_POST['id'])."'";
2. Dans le second cas (attribut contenant une URL), il n'y a aucune utilité pourhtmlspecialchars
; dans ces cas, vous devez encoder l'entrée en utilisant un schéma d'encodage URL, par exemple en utilisantrawurlencode
. De cette façon, un utilisateur ne peut pas insérerjavascript:
et al.Take a whitelist approach and only let through the chars which are good.
une liste noire manquera toujours quelque chose. +1En plus de l'excellente réponse de Cheekysoft:
Il n'y a pas vraiment de solution miracle pour empêcher l'injection HTML (par exemple, les scripts intersites), mais vous pourrez peut-être y parvenir plus facilement si vous utilisez une bibliothèque ou un système de modèles pour générer du HTML. Lisez la documentation pour cela pour savoir comment échapper aux choses de manière appropriée.
En HTML, les choses doivent être échappées différemment selon le contexte. Cela est particulièrement vrai des chaînes placées dans Javascript.
la source
Je serais certainement d'accord avec les articles ci-dessus, mais j'ai une petite chose à ajouter en réponse à la réponse de Cheekysoft, en particulier:
J'ai codé une petite fonction rapide que j'ai mise dans ma classe de base de données qui supprimera tout ce qui n'est pas un nombre. Il utilise preg_replace, donc il y a probablement une fonction un peu plus optimisée, mais cela fonctionne à la rigueur ...
Donc au lieu d'utiliser
j'utiliserais
et il exécuterait en toute sécurité la requête
Bien sûr, cela l'a juste empêché d'afficher la bonne ligne, mais je ne pense pas que ce soit un gros problème pour quiconque essaie d'injecter SQL dans votre site;)
la source
return preg_match('/^[0-9]+$/',$input) ? $input : 0;
Les contextes sont une pièce importante de ce puzzle. Quelqu'un qui envoie "1 OR 1 = 1" comme ID ne pose pas de problème si vous citez tous les arguments de votre requête:
Ce qui se traduit par:
ce qui est inefficace. Puisque vous échappez à la chaîne, l'entrée ne peut pas sortir du contexte de chaîne. J'ai testé cela jusqu'à la version 5.0.45 de MySQL, et l'utilisation d'un contexte de chaîne pour une colonne entière ne pose aucun problème.
la source
Fonctionne bien, encore mieux sur les systèmes 64 bits. Méfiez-vous des limites de vos systèmes pour traiter de grands nombres, mais pour les identifiants de base de données, cela fonctionne très bien 99% du temps.
Vous devriez également utiliser une seule fonction / méthode pour nettoyer vos valeurs. Même si cette fonction n'est qu'un wrapper pour mysql_real_escape_string (). Pourquoi? Parce qu'un jour, lorsqu'un exploit de votre méthode préférée de nettoyage des données est trouvé, vous n'aurez à le mettre à jour qu'à un seul endroit, plutôt qu'une recherche et un remplacement à l'échelle du système.
la source
pourquoi, oh POURQUOI, n'incluriez- vous pas des guillemets autour de l'entrée utilisateur dans votre instruction SQL? semble assez idiot de ne pas le faire! inclure des guillemets dans votre instruction SQL rendrait "1 ou 1 = 1" une tentative infructueuse, non?
alors maintenant, vous direz, "et si l'utilisateur inclut un devis (ou des guillemets doubles) dans l'entrée?"
Eh bien, solution facile pour cela: supprimez simplement les citations entrées par l'utilisateur. par exemple:
input =~ s/'//g;
. maintenant, il me semble de toute façon, que l'entrée de l'utilisateur serait sécurisée ...la source