Maison > Questions et réponses > le corps du texte
P粉8912379122023-08-23 00:21:40
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.
<?php mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); $mysqli = new mysqli("服务器", "用户名", "密码", "数据库名称"); $variable = $_POST["user-input"]; $stmt = $mysqli->prepare("INSERT INTO 表名 (列名) VALUES (?)"); // "s"表示数据库期望一个字符串 $stmt->bind_param("s", $variable); $stmt->execute();
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. P粉7712333362023-08-23 00:20:03
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
(fonctionne avec n'importe quel pilote de base de données pris en charge) :
Utilisez $stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');
$stmt->execute([ 'name' => $name ]);
foreach ($stmt as $row) {
// 对$row进行操作
}
(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 :
$result = $db->execute_query('SELECT * FROM employees WHERE name = ?', [$name]);
while ($row = $result->fetch_assoc()) {
// 对$row进行操作
}
execute_query()
Avant PHP8.1 :
$stmt = $db->prepare('SELECT * FROM employees WHERE name = ?');
$stmt->bind_param('s', $name); // 's'指定变量类型 => 'string'
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) {
// 对$row进行操作
}
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 connexionpg_prepare()
和pg_execute()
. 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çant $dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8mb4', 'user', 'password');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Ce doit PDOException
être, cependant, c'est la première ligne
ré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).
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 charset
charset
Mysqli
Pour mysqli, nous devons suivre la même routine :
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // 错误报告
$dbConnection = new mysqli('127.0.0.1', 'username', 'password', 'test');
$dbConnection->set_charset('utf8mb4'); // 字符集
Lorsque vous réussissez
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) :
$preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');
$preparedStatement->execute([ 'column' => $unsafeValue ]);
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.
// 值白名单 // $dir只能是'DESC',否则将为'ASC' if (empty($dir) || $dir !== 'DESC') { $dir = 'ASC'; }