Comment empêcher l'injection SQL en PHP ?
P粉022723606
2023-08-23 12:44:04
<p>Si une entrée utilisateur est insérée dans une requête SQL sans modification, l'application est vulnérable à l'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>, la requête devient : </p>
<pre class="brush:php;toolbar:false;">INSERT INTO `table` (`column`) VALUES('value');--')</pre>
<p>Que peut-on faire pour éviter que cela ne 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 des éléments suivants.
La fonction clé que vous devez lire est
mysqli::prepare
.De plus, comme d'autres l'ont suggéré, vous pourriez utiliser quelque chose comme PDO.
Veuillez noter que le cas sur lequel vous posez la question est assez simple, les cas plus complexes peuvent nécessiter des méthodes plus sophistiquées. Surtout :
mysql_real_escape_string
mysql_real_escape_string ne contient pas l'échappement requis. Dans ce cas, vous feriez mieux 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 soient toujours des données et >ne soient jamais interprétées comme des commandes par l'analyseur SQL. Il est possible de créer des instructions SQL en utilisant des parties de données correctement formatées, mais si vous comprenez parfaitement les détails, vous devez toujours utiliser des instructions préparées et des requêtes paramétrées. est une instruction SQL qui est envoyée et analysée par le serveur de base de données séparément de tout paramètre. De cette façon, il est impossible pour un attaquant d’injecter du SQL malveillant.
Vous avez essentiellement deux options pour y parvenir :PDO (pour tout pilote de base de données pris en charge) :
MySQLi (pour MySQL) :
À partir de PHP 8.2+, nous pouvons utiliser execute_query() pour préparer, lier des paramètres et exécuter des instructions SQL en une seule méthode :
Jusqu'à PHP8.1 :
pg_prepare()
pg_prepare()
etpg_execute() pour PostgreSQL). L’AOP est une option universelle.
Établissez correctement la connexion
AOP
Veuillez noter que lorsque vous utilisez PDO pour accéder à une base de données MySQL, les réelles instructions préparéesne sont pas utilisées par défaut. Pour résoudre ce problème, vous devez désactiver la simulation des instructions préparées. Un exemple de création d'une connexion à l'aide de PDO est :
Dans l'exemple ci-dessus, le mode erreur n'est pas strictement nécessaire, mais il est recommandé de l'ajouter . De cette façon, PDO vous informera de toutes les erreurs MySQL en lançant
.PDOException
PDOExceptionCependant, forcer
realsetAttribute()
est la première ligne setAttribute(), qui indique à PDO de désactiver les instructions préparées simulées et d'utiliser les instructions préparées. Cela garantit que les instructions et les valeurs ne sont pas analysées par PHP avant d'être envoyées au serveur MySQL (ne donnant aucune chance à un attaquant potentiel d'injecter du SQL malveillant).
dans les options du constructeur, il est important de noter que les versions "anciennes" de PHP (antérieures à 5.3.6)字符集
Bien que vous puissiez définir le charsetignorent silencieusement le paramètre charset
dans le DSN.Instructions
Lorsque vous réussissez
prepare
的SQL语句由数据库服务器解析和编译。通过指定参数(?
或命名参数,如上例中的:name
),您可以告诉数据库引擎您要过滤的位置。然后,当您调用execute
, l'instruction préparée sera combinée avec les valeurs des paramètres que vous spécifiez.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 à contenir une chaîne malveillante lorsqu'il crée le SQL à envoyer à la base de données. Ainsi, en envoyant le SQL réel séparément des paramètres, vous limitez le risque de vous retrouver avec quelque chose d'inattendu.
Tous les paramètres que vous envoyez lors de l'utilisation 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, les paramètres peuvent donc bien sûr également être traités comme des nombres). Dans l’exemple ci-dessus, si
$name
变量包含'Sarah'; DELETE FROMEmployees
结果只是搜索字符串"'Sarah'; DELETE FROMEmployees"
, et vous ne vous retrouvez pas avec une table vide.Un autre avantage de l'utilisation d'instructions préparées est que si vous exécutez la même instruction plusieurs fois au cours de la même session, elle ne sera analysée et compilée qu'une seule fois, augmentant ainsi la vitesse.
Oh, puisque vous avez demandé comment faire l'insertion, voici un exemple (en utilisant PDO) :
Les instructions préparées peuvent-elles être utilisées pour les requêtes dynamiques ?
Bien que vous puissiez toujours utiliser des instructions préparées avec des 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 non plus être paramétrées.
Pour ces scénarios spécifiques, la meilleure chose à faire est d'utiliser un filtre de liste blanche pour limiter les valeurs possibles.