116 votes

Est-ce que htmlspecialchars et mysql_real_escape_string protègent mon code PHP contre les injections ?

Plus tôt dans la journée, une question a été posée concernant stratégies de validation des entrées dans les applications web .

La meilleure réponse, au moment de la rédaction de ce document, suggère en PHP juste en utilisant htmlspecialchars y mysql_real_escape_string .

Ma question est la suivante : est-ce toujours suffisant ? Devons-nous en savoir plus ? Où ces fonctions s'effondrent-elles ?

240voto

Cheekysoft Points 16532

Lorsqu'il s'agit de requêtes de base de données, essayez toujours d'utiliser des requêtes paramétrées préparées. Le site mysqli y PDO les bibliothèques soutiennent cela. C'est infiniment plus sûr que d'utiliser des fonctions d'échappement telles que mysql_real_escape_string .

Sí, mysql_real_escape_string est en fait une simple fonction d'échappement des chaînes de caractères. Il ne s'agit pas d'une solution miracle. Tout ce qu'elle fait, c'est échapper les caractères dangereux afin qu'ils puissent être utilisés en toute sécurité dans une seule chaîne de requête. Toutefois, si vous n'aseptisez 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 la id contenait le vecteur d'attaque commun :

1 OR 1=1

Il n'y a pas de caractères à risque à encoder, donc il passera directement à travers le filtre d'échappement. Ce qui nous laisse :

SELECT fields FROM table WHERE id= 1 OR 1=1

Ce 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

Ce qui permet à l'attaquant de renvoyer les détails du premier administrateur dans cet exemple totalement 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 parce que nous n'avons pas vérifié qu'une variable que nous utilisions comme un nombre, était réellement 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 alphanumériques, etc. Mais lorsqu'il s'agit de SQL, il faut surtout tenir compte de 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 la base de données auraient su que 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, car il y a toute une sélection de différentes fonctions d'échappement liées au html, et il n'y a pas d'indications claires sur ce que font exactement ces fonctions.

Tout d'abord, si vous êtes à l'intérieur d'une balise HTML, vous avez de gros problèmes. Regardez

echo '<img src= "' . htmlspecialchars($_GET['imagesrc']) . '" />';

Nous sommes déjà à l'intérieur d'une balise HTML, donc nous n'avons pas besoin de < ou > pour faire quoi que ce soit de dangereux. Notre vecteur d'attaque pourrait simplement être javascript:alert(document.cookie)

Maintenant le HTML résultant ressemble à

<img src= "javascript:alert(document.cookie)" />

L'attaque passe directement à travers.

C'est pire. Pourquoi ? Parce que htmlspecialchars (lorsqu'il est appelé de cette façon) ne code que les guillemets doubles et non les simples. Ainsi, si nous avions

echo "<img src= '" . htmlspecialchars($_GET['imagesrc']) . ". />";

Notre attaquant maléfique peut maintenant 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 faut simplement sanctifier soi-même l'entrée. Si vous essayez de filtrer les mauvais caractères, vous échouerez à coup sûr. Adoptez une approche de liste blanche et ne laissez passer que les caractères qui sont bons. Regardez les Aide-mémoire sur les XSS pour des exemples de la diversité des vecteurs

Même si vous utilisez htmlspecialchars($string) en dehors des balises HTML, vous êtes toujours vulnérable aux vecteurs d'attaque par jeu de caractères multi-octets.

Le plus efficace 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 les UTF. Cependant, vous pouvez vous rabattre sur 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 de multi-octets, voir https://stackoverflow.com/a/12118602/1820

10voto

MarkR Points 37178

En plus de l'excellente réponse de Cheekysoft :

  • Oui, ils vous protègent, mais seulement s'ils sont utilisés correctement. Si vous ne les utilisez pas correctement, vous serez toujours vulnérable et vous risquez de rencontrer d'autres problèmes (par exemple, la corruption des 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 wrapper comme PEAR DB.
  • Assurez-vous que magic_quotes_gpc et magic_quotes_runtime sont désactivés à tout moment, et ne sont jamais activés accidentellement, même brièvement. Il s'agit d'une tentative précoce et profondément malavisée des développeurs de PHP pour prévenir les problèmes de sécurité (ce qui détruit les données).

Il n'existe pas vraiment de solution miracle pour empêcher l'injection de HTML (par exemple, le cross site scripting), mais vous pouvez y parvenir plus facilement si vous utilisez une bibliothèque ou un système de modèles pour produire du HTML. Lisez la documentation à ce sujet pour savoir comment échapper les choses de manière appropriée.

En HTML, les choses doivent être échappées différemment selon le contexte. C'est particulièrement vrai pour les chaînes de caractères placées en Javascript.

3voto

BrilliantWinter Points 64

Je suis tout à fait d'accord avec les messages précédents, mais j'ai une petite chose à ajouter en réponse à la réponse de Cheekysoft, plus précisément :

Quand il s'agit de requêtes de base de données, essayez toujours d'utiliser des requêtes préparées paramétrées. Les bibliothèques mysqli et PDO le supportent. Cette méthode est infiniment plus sûr que d'utiliser des fonctions telles que mysql_real_escape_string.

Oui, mysql_real_escape_string est effectivement juste une fonction d'échappement de de chaînes de caractères. Ce n'est pas une solution miracle. Tout ce qu'elle fait, c'est échapper les caractères dangereux dangereux afin qu'ils puissent être qu'ils puissent être utilisés en toute sécurité dans une seule chaîne de requête. Cependant, si vous n'aseptisez pas vos entrées entrées au préalable, vous serez vulnérables à certains vecteurs d'attaque.

Imaginez le SQL suivant :

$result = "SELECT fields FROM table WHERE id = ".mysql_real_escape_string($_POST['id']) ;

Vous devriez être capable de voir que c'est vulnérable à l'exploitation. Imaginez que le paramètre id contient l'attaque commune vecteur :

1 OU 1=1

Il n'y a pas de caractères à risque là-dedans pour à encoder, donc il passera directement à travers le filtre d'échappement. En quittant nous :

SELECT fields FROM table WHERE id = 1 OU 1=1

J'ai codé une petite fonction rapide que je mets dans ma classe de base de données et qui élimine tout ce qui n'est pas un nombre. Elle utilise preg_replace, donc il y a probablement une fonction un peu plus optimisée, mais ça marche à la rigueur...

function Numbers($input) {
  $input = preg_replace("/[^0-9]/","", $input);
  if($input == '') $input = 0;
  return $input;
}

Ainsi, au lieu d'utiliser

$result = "SELECT fields FROM table WHERE id = ".mysqlrealescapestring("1 OR 1=1") ;

J'utiliserais

$result = "SELECT fields FROM table WHERE id = ".Numbers("1 OR 1=1") ;

et il exécuterait en toute sécurité la requête

SELECT fields FROM table WHERE id = 111

Bien sûr, cela a juste empêché l'affichage de la bonne ligne, mais je ne pense pas que ce soit un gros problème pour celui qui essaie d'injecter du sql dans votre site ;)

2voto

Lucas Oman Points 9027

Les contextes sont une pièce importante de ce puzzle. Quelqu'un qui envoie "1 OU 1=1" comme identifiant 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'])."'"

Ce qui a pour conséquence :

SELECT fields FROM table WHERE id='1 OR 1=1'

qui est inefficace. Puisque vous échappez la chaîne, l'entrée ne peut pas sortir du contexte de la chaîne. J'ai testé cela jusqu'à la version 5.0.45 de MySQL, et l'utilisation d'un contexte de chaîne de caractères pour une colonne d'entiers ne pose aucun problème.

2voto

systematical Points 178
$result = "SELECT fields FROM table WHERE id = ".(INT) $_GET['id'];

Fonctionne bien, encore mieux sur les systèmes 64 bits. Attention aux limitations de votre système pour l'adressage de grands nombres, mais pour les identifiants de bases 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 est juste une enveloppe pour mysql_real_escape_string(). Pourquoi ? Parce qu'un jour, lorsqu'une exploitation de votre méthode préférée de nettoyage des données sera découverte, vous n'aurez qu'à la mettre à jour à un seul endroit, plutôt qu'à la rechercher et la remplacer dans tout le système.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X