Est-ce que htmlspecialchars et mysql_real_escape_string protègent mon code PHP contre l'injection?

StackOverflow https://stackoverflow.com/questions/110575

Question

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 réponse principale, au moment de la rédaction, suggère d'utiliser PHP simplement htmlspecialchars et mysql_real_escape_string.

Ma question est la suivante: est-ce toujours suffisant? Y at-il plus que nous devrions savoir? Où ces fonctions se décomposent-elles?

Était-ce utile?

La solution

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 prennent en charge. Ceci est infiniment plus sûr que d’utiliser des fonctions d'échappement telles que mysql_real_escape_string.

Oui, id n'est en réalité qu'une fonction d'échappement de chaîne. Ce n'est pas une balle magique. Tout ce que cela va faire, c'est é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 désinfectez pas vos entrées au préalable, vous serez vulnérable à certains vecteurs d'attaque.

Imaginez le code SQL suivant:

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

Vous devriez pouvoir voir que ceci est vulnérable à exploiter.
Imaginez que le paramètre 1 OR 1=1 contienne le vecteur d’attaque commun:

1 OR 1=1

Il n'y a aucun caractère risqué à encoder, il passera donc directement à travers le filtre qui s'échappe. Nous quittant:

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

Ce qui est un beau 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 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 car nous n'avons pas vérifié qu'une variable que nous utilisions sous forme de 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 alphanumériques, etc. Mais quand il s’agit de SQL, tenez surtout 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 htmlspecialchars() n'est pas un littéral valide.

Comme pour javascript:alert(document.cookie). C'est un champ de mines qui lui est propre.

Le vrai problème de PHP est qu’il contient toute une sélection de fonctions d’échappement liées au langage HTML et qu’il n’a pas d’indication claire sur les fonctions qui font quoi.

Tout d’abord, si vous êtes à l’intérieur d’une balise HTML, vous rencontrez de véritables problèmes. Regardez

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

Nous sommes déjà dans une balise HTML, nous n'avons donc pas besoin de < ou > faire quelque chose de dangereux. Notre vecteur d’attaque pourrait simplement être htmlspecialchars

Le résultat HTML ressemble maintenant à

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

L’attaque passe au travers.

Cela empire. Pourquoi? parce que htmlspecialchars($string) (quand on l’appelle ainsi) ne code que des guillemets et non des guillemets 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’ya pas de solution miracle, il vous suffit de santiser l’entrée vous-même. Si vous essayez de filtrer les mauvais personnages, vous échouerez sûrement. Adoptez une approche de liste blanche et laissez uniquement les caractères qui sont bons. Consultez la aide-mémoire XSS pour des exemples sur la manière dont les vecteurs peuvent être divers

Même si vous utilisez <=> en dehors des balises HTML, vous êtes toujours vulnérable aux vecteurs d'attaque de jeux de caractères multi-octets.

Le plus efficace possible 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 manière dont il gère UTF. Cependant, vous pourriez avoir recours à un codage plus limité, tel que ISO-8859-1, jusqu'à ce que l'utilisation d'IE6 diminue.

Pour une analyse plus approfondie des problèmes multi-octets, voir https://stackoverflow.com/a/12118602/1820

Autres conseils

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

  • Oui, ils vous garderont en sécurité, mais seulement s'ils sont utilisés correctement. Si vous ne les utilisez pas correctement, vous serez toujours vulnérable et vous risquez d’avoir d’autres problèmes (corruption de données, par exemple)
  • Utilisez plutôt des requêtes paramétrées (comme indiqué ci-dessus). Vous pouvez les utiliser par exemple 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, pas même brièvement. Il s’agit d’une tentative précoce et profondément erronée de la part des développeurs PHP d’empêcher les problèmes de sécurité (qui détruisent les données)

Il n’existe pas vraiment de solution miracle pour empêcher l’injection HTML (par exemple, le script intersite), mais vous pourrez peut-être le réaliser plus facilement si vous utilisez une bibliothèque ou un système de templates pour la sortie HTML. Lisez la documentation à ce sujet pour savoir comment échapper aux choses de manière appropriée.

En HTML, les éléments doivent être échappés différemment selon le contexte. Ceci est particulièrement vrai pour les chaînes placées dans Javascript.

Je suis tout à fait d'accord avec les commentaires ci-dessus, mais j'ai une petite chose à ajouter en réponse à la réponse de Cheekysoft, à savoir:

  

En ce qui concerne les requêtes de base de données,   toujours essayer d'utiliser préparé   requêtes paramétrées. Le mysqli et   Les bibliothèques PDO supportent cela. C'est   infiniment plus sûr que d'utiliser l'échappement   des fonctions telles que   mysql_real_escape_string.

     

Oui, mysql_real_escape_string est   effectivement juste une chaîne s'échappant   une fonction. Ce n'est pas une balle magique.   Tout ce qu'il va faire, c'est s'échapper dangereux   caractères afin qu'ils puissent être   sûr à utiliser dans une seule chaîne de requête.   Cependant, si vous ne désinfectez pas votre   entrées à l'avance, alors vous serez   vulnérable à certains vecteurs d'attaque.

     

Imaginez le code SQL suivant:

     

$ result = " champs SELECT dans la table   O id =   " .mysql_real_escape_string ($ _ POST ['id']);

     

Vous devriez pouvoir voir que c'est   vulnérable à exploiter. Imaginez l'identifiant   paramètre contenant l'attaque commune   vecteur:

     

1 OU 1 = 1

     

Il n'y a aucun caractère risqué là-bas pour   encode, donc il passera droit   à travers le filtre qui s'échappe. En quittant   nous:

     

SELECT champs de la table WHERE id = 1   OU 1 = 1

J'ai codé une petite fonction rapide que j'ai mise dans ma classe de base de données et qui supprimera tout ce qui n'est pas un nombre. Il utilise preg_replace, il y a donc un peu plus de fonctionnalités optimisées pour prob, mais ça marche ...

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

Donc au lieu d'utiliser

  

$ result = " champs SELECT de la table WHERE id = & "; mysqlrealescapestring (&"; 1 OU 1 = 1 & ";);

je voudrais utiliser

  

$ resultat = " champs SELECT de la table WHERE id = ". .Numéros (" 1 OU 1 = 1 ");

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

  

SELECT champs de la table WHERE id = 111

Bien sûr, cela l'a simplement empêché d'afficher la bonne ligne, mais je ne pense pas que ce soit un gros problème pour quiconque tente d'injecter SQL dans votre site;)

Les contextes constituent un élément important de ce puzzle. Quelqu'un envoie & Quot; 1 OU 1 = 1 & Quot; car l’ID n’est pas un problème si vous citez tous les arguments de votre requête:

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

Ce qui entraîne:

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

qui est inefficace. Comme 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 utiliser un contexte de chaîne pour une colonne entière ne pose 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 des limites de votre système concernant le traitement 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 est juste 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é, il vous suffit de le mettre à jour à un endroit, plutôt qu’à une recherche et un remplacement à l’échelle du système.

pourquoi, oh POURQUOI, pas inclure des guillemets autour des entrées de l'utilisateur dans votre déclaration 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, & "Que se passe-t-il si l'utilisateur inclut une citation (ou des guillemets) dans l'entrée? &";

bien, solution 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 la saisie de l'utilisateur serait sécurisée ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top