Est-ce que htmlspecialchars et mysql real escape string protègent mon code PHP de l'injection?
plus tôt aujourd'hui, une question a été posée concernant stratégies de validation des entrées dans les applications web .
la première réponse, au moment de l'écriture, suggère dans PHP
juste en utilisant htmlspecialchars
et mysql_real_escape_string
.
Ma question est: Est-ce toujours suffisant? Est-il plus que nous devrions savoir? Où ces fonctions se décomposent-elles?
6 réponses
en ce qui concerne les 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
le supportent. C'est infiniment plus sûr que d'utiliser des fonctions d'échappement telles que mysql_real_escape_string
.
Yes, mysql_real_escape_string
est effectivement juste une fonction d'échappement de chaîne. Ce n'est pas une balle magique. Tout ce qu'il va faire est d'échapper caractères dangereux afin qu'ils puissent être sûrs à utiliser dans une chaîne de requête simple. Toutefois, si vous n'avez pas stérilisez vos entrées à l'avance, alors 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 pouvoir voir que c'est vulnérable à exploiter.
Imaginez le paramètre id
contenant le vecteur d'attaque commun:
1 OR 1=1
il n'y a pas de caractères risqués à coder, donc il passera directement à travers le filtre qui s'échappe. Nous laissant:
SELECT fields FROM table WHERE id= 1 OR 1=1
qui est un joli vecteur D'injection SQL et permettrait à l'attaquant de retourner 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
qui permet à l'attaquant de retourner les détails du premier administrateur dans cet exemple complètement fictif.
bien que ces fonctions soient utiles, elles doivent être utilisées avec soin. Vous devez vous assurer que toutes les entrées sont validé dans une certaine mesure. Dans ce cas, nous voyons que nous pouvons être exploités parce que nous n'avons pas vérifié qu'une variable que nous utilisions comme un nombre était effectivement numérique. En PHP, vous devriez largement utiliser un ensemble de fonctions pour vérifier que les entrées sont des entiers, des flotteurs, des alphanumériques, etc. Mais quand il s'agit de SQL, tenir compte de la plupart de la valeur de la déclaration préparée. Le code ci-dessus aurait été sécurisé s'il s'agissait d'une déclaration préparée car les fonctions de la base de données auraient su que 1 OR 1=1
n'est pas valide littéral.
comme pour htmlspecialchars()
. C'est un champ de mines qui lui est propre.
il y a un vrai problème en PHP en ce sens qu'il a toute une sélection de différentes fonctions d'échappement liées au html, et aucune indication claire sur les fonctions qui font quoi exactement.
tout d'abord, si vous êtes à l'intérieur d'une balise HTML, vous êtes vraiment en difficulté. Regardez
echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';
nous sommes déjà dans une balise HTML, donc nous n'avons pas besoin de < ou > faire quelque chose de dangereux. Notre Vecteur d'attaque pourrait être javascript:alert(document.cookie)
maintenant HTML résultant ressemble à
<img src= "javascript:alert(document.cookie)" />
l'attaque est directe.
ça empire. Pourquoi? parce que htmlspecialchars
(quand on l'appelle de cette façon) n'encode que les guillemets doubles et pas les guillemets simples. Donc si nous avions
echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";
notre attaquant diabolique peut maintenant injecter de 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 Balle magique, vous avez juste à santiser l'entrée vous-même. Si vous essayez de filtrer les mauvais caractères, vous aurez sûrement échouer. Prendre une approche de liste blanche et ne laisser passer que les caractères qui sont bons. Regardez la XSS cheat sheet pour des exemples sur la façon dont divers vecteurs peuvent être
même si vous utilisez htmlspecialchars($string)
en dehors des balises HTML, vous êtes toujours vulnérable aux multi-octets les vecteurs d'attaque du charset.
La plus efficace, vous pouvez être est d'utiliser une 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 pourriez vous rabattre sur un encodage plus limité, tel que ISO-8859-1, jusqu'à ce que L'utilisation de IE6 diminue.
Pour une étude plus approfondie de la multi-octets problèmes, consultez la section https://stackoverflow.com/a/12118602/1820
en plus de L'excellente réponse de Cheekysoft:
- Oui, ils vous protégeront, mais seulement s'ils sont utilisés absolument correctement. De les utiliser correctement et vous serez toujours vulnérable, et peut avoir d'autres problèmes (par exemple la corruption de données)
- veuillez utiliser des requêtes paramétrées à la place (comme indiqué ci-dessus). Vous pouvez les utiliser par exemple via PDO ou via un emballage comme PEAR DB
- assurez-vous que magic_quotes_gpc et magic_quotes_runtime est éteint à tout moment, et ne jamais être accidentellement allumé, pas même brièvement. Il s'agit d'une tentative précoce et profondément erronée par les développeurs de PHP pour prévenir les problèmes de sécurité (qui détruit les données)
il n'y a pas vraiment de solution miracle pour empêcher L'injection HTML (par exemple, les scripts cross site), mais vous pourriez être en mesure de l'atteindre plus facilement si vous utilisez une bibliothèque ou un système de Templiers pour la sortie HTML. Lire la documentation pour c'est pour échapper aux choses de façon appropriée.
en HTML, les choses doivent être échappées différemment selon le contexte. Cela est particulièrement vrai pour les chaînes placé en Javascript.
je serais certainement d'accord avec les poteaux ci-dessus, mais j'ai une petite chose à ajouter en réponse à la réponse de Cheekysoft, spécifiquement:
en ce qui concerne les requêtes de base de données, toujours essayer et utiliser préparé paramétrer les requêtes. Le mysqli et Les bibliothèques AOP soutiennent cela. C'est infiniment plus sûr que d'utiliser échapper des fonctions telles que mysql_real_escape_string.
Oui, mysql_real_escape_string is effectivement juste un échappement de la chaîne fonction. Ce n'est pas une balle magique. Tous il va faire est de s'échapper dangereux personnages afin qu'ils puissent être sûr à utiliser dans une chaîne de requête unique. Cependant, si vous n ' assainissez pas votre les entrées à l'avance, alors vous serez en vulnérables à certains vecteurs d'attaque.
imaginez le SQL suivant:
$result = "SÉLECTIONNEZ les champs DE la table Where id = ".mysql_real_escape_string ($_POST['id']);
vous devriez pouvoir voir que c'est vulnérable à exploiter. Imaginez l'id paramètre contenant l'attaque commune vecteur:
1 ou 1=1
il n'y a pas de risque Encoder, pour qu'il passe tout droit à travers la trajectoire de filtre. Partant us:
sélectionner les champs du tableau où id = 1 Ou 1 = 1
j'ai codé une petite fonction rapide que j'ai mise dans mon classe de base de données qui élimine tout ce qui n'est pas un nombre. Il utilise preg_replace, donc il y a prob un peu plus de fonction optimisée, mais il fonctionne dans un pincement...
function Numbers($input) {
$input = preg_replace("/[^0-9]/","", $input);
if($input == '') $input = 0;
return $input;
}
donc au lieu d'utiliser
$result = " SELECT fields FROM table WHERE id =".mysqlrealescapestring ("1 ou 1=1");
je voudrais utiliser
$result = " SELECT fields FROM table WHERE id = ".Nombres ("1 ou 1=1");
et il lancerait en toute sécurité la requête
sélectionner les champs du tableau où id = 111
bien sûr, cela vient de l'empêcher d'afficher la bonne rangée, mais je ne pense pas que ce soit un gros problème pour celui qui essaie d'injecter sql dans votre site ;)
une pièce importante de ce puzzle est les contextes. Quelqu'un qui envoie "1 ou 1=1" comme ID n'est pas un problème si vous citez chaque argument dans votre requête:
SELECT fields FROM table WHERE id='".mysql_real_escape_string($_GET['id'])."'"
qui se traduit par:
SELECT fields FROM table WHERE id='1 OR 1=1'
ce qui est inefficace. Puisque vous échappez à la chaîne, l'entrée ne peut pas sortir du contexte string. J'ai testé ceci jusqu'à la version 5.0.45 de MySQL, et l'utilisation d'un contexte de chaîne pour une colonne entière ne cause aucun problème.
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];
fonctionne bien, encore mieux sur les systèmes 64 bits. Méfiez-vous de vos limitations de systèmes sur l'adressage de grands nombres cependant, mais pour les ID de base de données cela fonctionne très 99% du temps.
vous devez utiliser une seule fonction/méthode pour nettoyer vos valeurs aussi bien. Même si cette fonction n'est qu'un wrapper pour mysql_real_escape_string(). Pourquoi? Parce qu'un jour où une exploitation de votre méthode préférée de nettoyage des données est trouvé, vous n'avez qu'à mettre à jour un plutôt qu'une recherche et un remplacement à l'échelle du système.
Pourquoi, oh pourquoi, est-ce que vous et non incluez des guillemets sur les entrées de l'utilisateur dans votre énoncé sql? semble tout à fait stupide de ne pas! y compris des citations dans votre instruction sql rendrait "1 ou 1=1" une tentative infructueuse, non?
alors maintenant, vous allez dire, "que si l'utilisateur comporte une citation (ou des guillemets) dans l'entrée?"
Eh bien, correction facile pour cela: il suffit de supprimer les citations entrées par l'utilisateur. par exemple: input =~ s/'//g;
. maintenant, il me semble de toute façon, que l'utilisateur les entrées seraient sécurisées...