Comment prévenir les attaques par injection SQL en PHP ?
P粉939473759
2023-08-22 10:23:01
<p>Si une entrée utilisateur est insérée dans une requête SQL sans modification, l'application devient vulnérable aux attaques par injection SQL, comme dans l'exemple suivant : </p>
<pre class="lang-php Prettyprint-override"><code>$unsafe_variable = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ("$unsafe_variable')");
</code></pre>
<p>Cela est dû au fait que l'utilisateur peut saisir quelque chose comme <code>value'); DROP TABLE table;--</code> et la requête deviendra : </p>
<pre class="brush:php;toolbar:false;">INSERT INTO `table` (`column`) VALUES('value');--')</pre>
<p>Comment puis-je empêcher que cela se produise ? </p>
Pour utiliser des requêtes paramétrées, vous devez utiliser Mysqli ou PDO. Pour réécrire votre exemple en utilisant mysqli, nous aurions besoin d'un code similaire au suivant.
Les fonctions clés que vous voudrez peut-être lire sont
mysqli::prepare
.De plus, comme d'autres l'ont suggéré, vous trouverez peut-être plus utile/plus facile d'utiliser une couche d'abstraction de niveau supérieur comme PDO.
Veuillez noter que la situation que vous avez mentionnée est assez simple, des situations plus complexes peuvent nécessiter des approches plus sophistiquées. Surtout :
mysql_real_escape_string
. Dans ce cas, il est préférable de transmettre les entrées de l'utilisateur via une liste blanche pour garantir que seules les valeurs « sûres » sont autorisées.Quelle que soit la base de données que vous utilisez, la bonnefaçon d'éviter les attaques par injection SQL est de séparer les données du SQL afin que les données restent sous la forme de données qui ne seront jamais interprétées comme des commandes par l'analyseur SQL. . Il est possible de créer des instructions SQL avec des parties de données correctement formatées, mais si vous ne comprenez pas complètement les détails, vous devez toujours utiliser des instructions préparées et des requêtes paramétrées. Il s'agit d'instructions SQL envoyées séparément de tout paramètre et analysées par le serveur de base de données. De cette façon, les attaquants ne peuvent pas injecter du SQL malveillant. Il existe essentiellement deux manières d'y parvenir :
Utilisation de
PDO MySQLi-
(fonctionne avec n'importe quel pilote de base de données pris en charge) : Utilisez
(pour MySQL) : Depuis PHP 8.2+, nous pouvons utiliser les méthodes
pour préparer, lier des paramètres et exécuter des instructions SQL :
execute_query()
Avant PHP8.1 :Si vous vous connectez à une base de données autre que MySQL, vous pouvez vous référer à la deuxième option spécifique au pilote (pour PostgreSQL par exemple, vous pouvez utiliser
). L’AOP est une option universelle.Établissez correctement la connexion
AOPpg_prepare()
和pg_execute()
Veuillez noter que lorsque vous utilisez
PDOpour accéder à une base de données MySQL, les véritables
instructions préparées ne sont pas utilisées par défaut. Pour résoudre ce problème, vous devez désactiver la simulation des instructions préparées. Voici un exemple de création d'une connexion à l'aide de PDO :
. Dans l'exemple ci-dessus, le mode erreur n'est pas strictement obligatoire, mais il est recommandé de l'ajouter . De cette façon, PDO vous informera de toutes les erreurs MySQL en lançantCe doit
, qui indique à PDO de désactiver les instructions préparées simulées et d'utiliser des instructions préparéesPDOException
être, cependant, c'est la première ligneréelles . Cela garantit que les instructions et les valeurs ne sont pas analysées par PHP avant d'être envoyées au serveur MySQL (les attaquants potentiels ne peuvent donc pas injecter du SQL malveillant).
dans le DSN.setAttribute()
Bien que vous puissiez le définir dans les options du constructeur, il est important de noter que les "anciennes" versions de PHP (antérieures à 5.3.6) ignorent silencieusement le paramètre charsetcharset
Mysqli Pour mysqli, nous devons suivre la même routine :, l'instruction préparée sera combinée avec les valeurs des paramètres spécifiés.
La chose importante ici est que la valeur du paramètre est combinée avec l'instruction compilée, pas avec la chaîne SQL. L'injection SQL fonctionne en incitant un script à inclure une chaîne malveillante lors de la création du code SQL à envoyer à la base de données. Par conséquent, en envoyant le SQL réel séparément des paramètres, vous limitez le risque de résultats inattendus.
Tous les paramètres envoyés à l'aide d'instructions préparées seront traités comme des chaînes (bien que le moteur de base de données puisse effectuer certaines optimisations sur les paramètres, de sorte que les paramètres peuvent finir par être des nombres). Dans l'exemple ci-dessus, si
$name
变量包含'Sarah'; DELETE FROM employees
,结果将仅是搜索字符串"'Sarah'; DELETE FROM employees"
vous n'obtiendriez pas une table vide.Un autre avantage de l'utilisation d'instructions préparées est que si la même instruction est exécutée plusieurs fois au cours de la même session, elle ne sera analysée et compilée qu'une seule fois, améliorant ainsi une certaine vitesse.
Oh, puisque vous avez demandé comment opérer sur les inserts, voici un exemple (en utilisant PDO) :
Les instructions préparées sont-elles adaptées aux requêtes dynamiques ?
Bien que vous puissiez toujours utiliser des instructions préparées pour les paramètres de requête, la structure de la requête dynamique elle-même ne peut pas être paramétrée et certaines fonctions de requête ne peuvent pas être paramétrées.
Pour ces scénarios spécifiques, la meilleure pratique consiste à utiliser un filtre de liste blanche pour limiter les valeurs possibles.