web-dev-qa-db-fra.com

Dois-je me prémunir contre l'injection SQL si j'utilise une liste déroulante?

Je comprends que vous ne devez JAMAIS faire confiance à l'entrée utilisateur d'un formulaire, principalement en raison du risque d'injection SQL.

Cependant, cela s'applique-t-il également à un formulaire dont la seule entrée provient d'une ou de plusieurs listes déroulantes (voir ci-dessous)?

Je sauve le $_POST['size'] à une session qui est ensuite utilisée sur tout le site pour interroger les différentes bases de données (avec une requête mysqli Select) et toute injection SQL les endommagerait (voire les supprimerait).

Il n'y a pas de zone de saisie utilisateur tapée pour interroger les bases de données, uniquement des listes déroulantes.

<form action="welcome.php" method="post">
<select name="size">
  <option value="All">Select Size</option> 
  <option value="Large">Large</option>
  <option value="Medium">Medium</option>
  <option value="Small">Small</option>
</select>
<input type="submit">
</form>
95
Tatters

Vous pouvez faire quelque chose d'aussi simple que l'exemple suivant pour vous assurer que la taille publiée correspond à ce que vous attendez.

$possibleOptions = array('All', 'Large', 'Medium', 'Small');

if(in_array($_POST['size'], $possibleOptions)) {
    // Expected
} else {
    // Not Expected
}

Utilisez ensuite mysqli_ * si vous utilisez une version de php> = 5.3.0 que vous devriez être, pour sauvegarder votre résultat. S'il est utilisé correctement, cela aidera à l'injection sql.

69

Oui, vous devez vous protéger contre cela.

Permettez-moi de vous montrer pourquoi, en utilisant la console de développement de Firefox:

i've edited one of the values in the dropdown to be a drop table statement

Si vous ne nettoyez pas ces données, votre base de données sera détruite. (Ce n'est peut-être pas une instruction SQL totalement valide, mais j'espère avoir fait comprendre mon point de vue.)

Ce n'est pas parce que vous avez limité les options disponibles dans votre liste déroulante que vous avez limité les données que je peux envoyer à votre serveur.

Si vous avez essayé de restreindre davantage ce comportement en utilisant votre page, mes options incluent la désactivation de ce comportement, ou simplement l'écriture d'une demande HTTP personnalisée sur votre serveur qui imite de toute façon cette soumission de formulaire. Il y a un outil appelé curl utilisé pour cela, et je pense la commande pour soumettre cette injection SQL de toute façon ressemblerait à ceci:

curl --data "size=%27%29%3B%20DROP%20TABLE%20*%3B%20--"  http://www.example.com/profile/save

(Ce n'est peut-être pas une commande curl totalement valide, mais encore une fois, j'espère avoir fait comprendre mon point de vue.)

Je réitère donc:

NE JAMAIS faire confiance à l'entrée utilisateur. TOUJOURS vous protéger.

Ne présumez pas qu'une entrée d'utilisateur est jamais sûre. Il est potentiellement dangereux même s'il arrive par d'autres moyens qu'un formulaire. Rien de tout cela n'est jamais assez fiable pour renoncer à vous protéger contre l'injection SQL.

194
doppelgreener

Comme cette question a été taguée avec sql-injection , voici une réponse concernant ce type d'attaque particulier:

Comme on vous l'a dit dans les commentaires, vous devez utiliser des instructions préparées pour chaque requête impliquant des données variables, avec aucune exception .

Indépendamment de tout contenu HTML!
Il est essentiel de comprendre que les requêtes SQL doivent être formatées correctement indépendamment de tout facteur externe, que ce soit une entrée HTML ou autre.

Bien que vous puissiez utiliser la liste blanche suggérée dans d'autres réponses à des fins de validation d'entrée, cela ne devrait pas affecter les actions liées à SQL - elles doivent rester la même chose, peu importe si vous avez validé l'entrée HTML ou non. Cela signifie que vous devez toujours utiliser des instructions préparées lors de l'ajout de variables dans la requête.

Vous trouverez ici une explication approfondie, pourquoi les instructions préparées sont un must et comment les utiliser correctement et où elles ne s'appliquent pas et que faire dans un tel cas: Le Guide de l'auto-stoppeur pour la protection par injection SQL

En outre, cette question a été marquée avec mysqli . Généralement par accident, je présume, mais de toute façon, je dois vous avertir que le mysqli brut n'est pas une substitution adéquate pour les anciennes fonctions mysq_ * . Tout simplement parce que s'il est utilisé dans l'ancien style, il n'ajoutera aucune sécurité. Bien que la prise en charge des instructions préparées soit douloureuse et gênante, au point cette moyenne PHP utilisateur est tout simplement incapable de les essayer du tout. Ainsi, si aucun ORM ou une sorte de bibliothèque d'abstraction n'est l'option, alors PDO est votre seul choix.

45
Your Common Sense

Oui.

Tout le monde peut usurper n'importe quoi pour les valeurs qui sont réellement envoyées -

Donc, pour valider les menus déroulants, vous pouvez simplement vérifier que la valeur avec laquelle vous travaillez est dans le menu déroulant - quelque chose comme ça serait la meilleure façon (la plus saine paranoïaque):

if(in_array($_POST['ddMenu'], $dropDownValues){

    $valueYouUseLaterInPDO = $dropDownValues[array_search("two", $arr)];

} else {

    die("effin h4x0rs! Keep off my LAMP!!"); 

}
12
rm-vanda

Une façon de se protéger contre les utilisateurs qui modifient vos listes déroulantes à l'aide de la console est de n'utiliser que des valeurs entières. Ensuite, vous pouvez valider que la valeur POST contient un entier et utiliser un tableau pour le convertir en texte si nécessaire. Par exemple:

<?php
// No, you don't need to specify the numbers in the array but as we're using them I always find having them visually there helpful.
$sizes = array(0 => 'All', 1 => 'Large', 2 => 'Medium', 3 => 'Small');
$size = filter_input(INPUT_POST, "size", FILTER_VALIDATE_INT);

echo '<select name="size">';
foreach($sizes as $i => $s) {
    echo '<option value="' . $i . '"' . ($i == $size ? ' selected' : '') . '>' . $s . '</option>';
}
echo '</select>';

Ensuite, vous pouvez utiliser $size dans votre requête en sachant qu'elle ne contiendra que FALSE ou un entier.

8
Styphon

Les autres réponses couvrent déjà ce que vous devez savoir. Mais peut-être que cela aide à clarifier un peu plus:

Il y a DEUX CHOSES que vous devez faire:

1. Validez les données du formulaire.

Comme réponse de Jonathan Hobbs le montre très clairement, le choix de l'élément html pour l'entrée de formulaire ne fait aucun filtrage fiable pour vous.

La validation est généralement effectuée d'une manière qui ne modifie pas les données, mais qui affiche à nouveau le formulaire, avec les champs marqués "Veuillez corriger cela".

La plupart des frameworks et CMS ont des constructeurs de formulaires qui vous aident dans cette tâche. Et pas seulement cela, ils aident également contre CSRF (ou "XSRF"), qui est une autre forme d'attaque.

2. Sanitize/Escape variables dans les instructions SQL.

.. ou laissez les déclarations préparées faire le travail pour vous.

Si vous créez une instruction (My) SQL avec des variables, fournies par l'utilisateur ou non, vous devez vous échapper et citer ces variables.

Généralement, toute variable de ce type que vous insérez dans une instruction MySQL doit être soit une chaîne, soit quelque chose qui PHP peut être transformé de manière fiable en une chaîne que MySQL peut digérer. Par exemple, des nombres.

Pour les chaînes, vous devez ensuite choisir l'une des méthodes pour échapper à la chaîne, c'est-à-dire remplacer tous les caractères qui auraient des effets secondaires dans MySQL.

  • Dans la vieille école MySQL + PHP, mysql_real_escape_string () fait le travail. Le problème est qu'il est beaucoup trop facile d'oublier, vous devez donc absolument utiliser des instructions préparées ou des générateurs de requêtes.
  • Dans MySQLi, vous pouvez utiliser des instructions préparées.
  • La plupart des frameworks et CMS fournissent des générateurs de requêtes qui vous aident dans cette tâche.

Si vous avez affaire à un nombre, vous pouvez omettre l'échappement et les guillemets (c'est pourquoi les instructions préparées permettent de spécifier un type).

Il est important de souligner que vous échappez aux variables pour l'instruction SQL, et NON pour la base de données elle-même. La base de données stockera la chaîne d'origine, mais l'instruction a besoin d'une version d'échappement.

Que se passe-t-il si vous en omettez un?

Si vous n'utilisez pas la validation de formulaire , mais que vous désinfectez votre entrée SQL, vous pourriez voir toutes sortes de mauvaises choses se produire, mais vous ne le ferez pas voir injection SQL! (*)

Tout d'abord, cela peut amener votre demande dans un état que vous n'aviez pas prévu. Par exemple. si vous voulez calculer l'âge moyen de tous les utilisateurs, mais qu'un utilisateur a donné "aljkdfaqer" pour l'âge, votre calcul échouera.

Deuxièmement, il peut y avoir toutes sortes d'autres attaques par injection dont vous devez tenir compte: par ex. l'entrée utilisateur peut contenir du javascript ou d'autres éléments.

Il peut encore y avoir des problèmes avec la base de données: par exemple. si un champ (colonne de table de base de données) est limité à 255 caractères et que la chaîne est plus longue que cela. Ou si le champ accepte uniquement des nombres et que vous essayez d'enregistrer une chaîne non numérique à la place. Mais ce n'est pas de "l'injection", c'est juste "planter l'application".

Mais, même si vous avez un champ de texte libre où vous autorisez toute entrée sans aucune validation, vous pouvez toujours l'enregistrer dans la base de données comme ça, si vous l'échappez correctement quand il va à une instruction de base de données. Le problème survient lorsque vous souhaitez utiliser cette chaîne quelque part.

(*) ou ce serait quelque chose de vraiment exotique.

Si vous n'échappez pas aux variables pour les instructions SQL , mais que vous avez validé l'entrée du formulaire, vous pouvez toujours voir les mauvaises choses se produire.

Premièrement, vous risquez que lorsque vous enregistrez des données dans la base de données et les rechargez, ce ne seront plus les mêmes données, "perdues en traduction".

Deuxièmement, cela peut entraîner des instructions SQL non valides et ainsi bloquer votre application. Par exemple. si une variable contient un guillemet ou un caractère guillemet double, selon le type de guillemet que vous utilisez, vous obtiendrez une déclaration MySQL invalide.

Troisièmement, il peut toujours provoquer une injection SQL.

Si votre entrée utilisateur à partir de formulaires est déjà filtrée/validée, intentionnel L'injection SQl peut devenir moins probable, SI votre entrée est réduite à une liste d'options codée en dur, ou si elle est limitée à des nombres. Mais toute entrée de texte libre peut être utilisée pour l'injection SQL, si vous n'échappez pas correctement aux variables dans les instructions SQL.

Et même si vous n'avez aucune entrée de formulaire, vous pouvez toujours avoir des chaînes de toutes sortes de sources: lecture à partir du système de fichiers, grattée sur Internet, etc. Personne ne peut garantir que ces chaînes sont sûres.

7
donquixote

Votre navigateur web ne "sait" pas qu'il reçoit une page de php, il ne voit que du html. Et la couche http en sait encore moins que cela. Vous devez être capable de gérer presque n'importe quel type d'entrée qui peut traverser la couche http (heureusement pour la plupart des entrées, php donnera déjà une erreur). Si vous essayez d'empêcher les demandes malveillantes de gâcher votre base de données, vous devez supposer que le gars à l'autre bout sait ce qu'il fait et qu'il n'est pas limité à ce que vous pouvez voir dans votre navigateur dans des circonstances normales ( sans parler de ce que vous pouvez jouer avec les outils de développement d'un navigateur). Alors oui, vous devez répondre à toute entrée de votre liste déroulante, mais pour la plupart des entrées, vous pouvez donner une erreur.

6
Nameless One

Le fait que vous ayez restreint l'utilisateur à n'utiliser que des valeurs d'une certaine liste déroulante n'est pas pertinent. Un utilisateur technique peut capturer la requête http envoyée à votre serveur avant qu'il ne quitte son réseau, la modifier à l'aide d'un outil tel qu'un serveur proxy local, puis la poursuivre sur son chemin. À l'aide de la demande modifiée, ils peuvent envoyer des valeurs de paramètres qui ne sont pas celles que vous avez spécifiées dans la liste déroulante. Les développeurs doivent avoir la mentalité que les restrictions client sont souvent dénuées de sens, car tout ce qui concerne un client peut être modifié. La validation du serveur est requise à chaque single point d'entrée des données client. Les attaquants comptent sur la naïveté des développeurs dans ce seul aspect.

6
Jason Higgins

Il est préférable d'utiliser une requête paramétrée afin de se prémunir contre l'injection SQL. Dans ce cas, l'apparence de la requête serait la suivante:

SELECT * FROM table WHERE size = ?

Lorsque vous fournissez une requête comme celle-ci avec du texte non vérifié pour son intégrité (l'entrée n'est pas validée sur le serveur) et qui contient du code d'injection SQL, elle sera traitée correctement. En d'autres termes, la demande entraînera quelque chose comme cela dans la couche de base de données:

SELECT * FROM table WHERE size = 'DROP table;'

Cela sélectionnera simplement 0 résultats lors de son retour, ce qui rendra la requête inefficace pour endommager la base de données sans avoir besoin d'une liste blanche, d'une vérification de vérification ou d'autres techniques. Veuillez noter qu'un programmeur responsable fera de la sécurité dans les couches, et validera souvent en plus de paramétrer les requêtes. Cependant, il y a très peu de raisons de ne pas paramétrer vos requêtes du point de vue des performances et la sécurité ajoutée par cette pratique est une bonne raison de vous familiariser avec les requêtes paramétrées.

5
Dan Smith

Un pirate peut contourner complètement le navigateur, y compris la vérification du formulaire Javascript, en envoyant une demande à l'aide de Telnet. Bien sûr, il va regarder le code de votre page html pour obtenir les noms de champs qu'il doit utiliser, mais à partir de ce moment, "tout va" pour lui. Vous devez donc vérifier toutes les valeurs soumises sur le serveur comme si elles ne provenaient pas de votre page html.

4
user1452686

Tout ce qui est soumis à partir de votre formulaire arrive sur votre serveur sous forme de texte à travers les fils. Rien n'empêche quiconque de créer un bot pour imiter le client ou le taper à partir d'un terminal s'il le souhaite. Ne présumez jamais que parce que vous avez programmé le client, il agira comme vous le pensez. C'est vraiment facile à usurper.

Exemple de ce qui peut et va se produire lorsque vous faites confiance au client.

4
GenericJam