回答

收藏

如何防止 PHP 中的 SQL 注入?

技术问答 技术问答 325 人阅读 | 0 人回复 | 2023-09-11

如果用户输入未经修改就插入 SQL 在查询中,应用程序很容易受到影响SQL 注射攻击,如下例所示:
) r5 ~4 v9 f8 z3 ?
    $unsafe_variable = $_POST['user_input']; mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");( t& |9 i& ~/ a3 G. b, z2 c
因为用户可以输入相似之处value'); DROP TABLE table;--查询的内容变为:4 }& n3 _, W) R
    INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')& ^6 D$ Q6 s  E+ x1 u' X: s  M
能做些什么来防止这种情况发生?3 I, M6 C6 _5 u- ^/ s8 s/ B
                                                               
) P. _! P" W6 n, x1 [7 g    解决方案:                                                               
2 f+ F- f8 c$ T1 x9 s/ D( T: |' ^                                                                无论使用哪个数据库,都要避免 SQL 注入攻击正确方法是将数据与 SQL 分离,保持数据,永远不会被 SQL 解析器解释命令。可以使用正确格式的数据部分创建 SQL 句子,但如果你不是完全了解细节,要始终如一使用准备好的句子和参数查询。这些都是与任何参数分开发送到数据库服务器并由其分析的 SQL 语句。这样,攻击者就不可能注入恶意 SQL。, v" y( I) T& g3 Z9 m5 ]' |
实现这一点基本上有两种选择:
' x  k, q; w2 u, C  P[ol]使用PDO(任何支持的数据库驱动程序):[/ol]```php
2 G# n# [6 E7 A3 V- h                $stmt = $pdo->prepare(‘SELECT * FROM employees WHERE name = :name’);
! s1 G3 {) {4 O: Y$stmt->execute([ 'name' => $name ]);foreach ($stmt as $row)      / Do something with $row}```5 W& A4 }8 m( \+ J+ w! r% U9 v' U
[ol]使用MySQLi(用于 MySQL):[/ol]```php! l0 E9 x! w, D5 z
                $stmt = $dbConnection->prepare(‘SELECT * FROM employees WHERE name = ?’);
) }  {9 K2 y6 I0 j# S: p. l& v  H                $stmt->bind_param(‘s’,$name); // ‘s’ specifies the variable type => ‘string’. v# n: T2 L* {! M% d! y5 t8 ?# _; M
$stmt->execute();$result = $stmt->get_result();while ($row = $result->fetch_assoc()Do something with $row}```
+ M" ?" I, ^1 R+ k* B& z若要连接 MySQL 以外的数据库可参考驱动程序的第二个选项(例如,pg_prepare()对于pg_execute()PostgreSQL)。PDO 是通用选项。" a" d: z( }+ e
连接设置正确使用时请注意PDO*访问 MySQL 数据库时**,默认情况下不使用实际准备好的句子。要解决这个问题,你必须禁止模拟准备好的句子。使用PDO*创建连接的例子是:# Z* J9 X1 i: [5 b& U: g
    $dbConnection = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8','user','password');$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);$dbConnection->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);* g+ p$ L( H, B8 q7 M3 H
在上面的示例中,错误模式不是绝对必要的,但建议添加。这样脚本就不会了Fatal Error当出现问题时停止。它给了开发人员处理问题的机会。n as s 的catch任何错误。throw``PDOException
5 Q. ?3 B' Z2 g' D然而,第一行是强制性setAttribute(),它告诉 PDO 禁用模拟预准备句,使用模拟预准备句真正的准备句子。这确保了句子和值发送到 MySQL 服务器以前不会被  PHP 分析(使可能的攻击者没有机会注入恶意 SQL)。) b: n8 D# q. M- a- w0 r! K
尽管您可以charset 设置在构造函数的选项中,但重要的是版 很重要PHP(5.3.在6 之前)默默忽略了 DSN 中的 charset 参数。1 Q! r9 A$ U" _- \( o1 f2 o
解释你传递给你SQL 语句prepare由数据库服务器进行分析和编译。指定参数(如上例中的参数或命名参数:name),您可以告诉数据库引擎您要过滤的位置。然后,当您呼叫 时execute,准备好的句子与您指定的参数值相结合。( S) X! p' p% U/ T& y* P6 M- N
参数值与编译句子相结合,而不是 SQL 字符串。SQL 注入的工作原理是创建要发送到数据库的 SQL 诱导脚本包含恶意字符串。因此,通过实际 SQL 单独发送参数,你可以限制结束你不想要的风险。
: \0 j" Q, ]6 W+ p您在使用准备好的句子时发送的任何参数都将被视为字符串(尽管数据库引擎可能会进行一些优化,但参数最终可能以数字形式结束)。述示例中,如果$name变量包含'Sarah'; DELETE FROM employees结果只是对 string 的搜索"'Sarah'; DELETE FROM employees",你最终不会得到一个空 table。4 [* Z+ S8 Q  o) J
使用准备句子的另一个好处是,如果你在同一个会话中多次执行相同的句子,它只会被分析和编译一次,从而给你带来一些速度提高。, k- Z3 t& q% r/ m. i* G4 d" }
哦,既然你问过如何插入,这里有一个例子(使用 PDO):
0 M. [, a" l* ^4 ^# p; M/ F2 e

    . H; q/ T6 l) R4 A+ O9 o
  • $preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');$preparedStatement->execute([ 'column' => $unsafeValue);(//);code]准备好的句子能用于动态查询吗?虽然您仍然可以使用准备好的语句来查询参数,但动态查询本身的结构不能参数化,某些查询功能也不能参数化。
    ! O+ ^- N7 u/ y1 c$ [
  • 对于这些特定场景,最好的办法是使用限制可能值的白名单过滤器。[code]// Value whitelist// $dir can only be 'DESC',otherwise it will be 'ASC'if (empty($dir) || $dir !== 'DESC {   $dir = 'ASC';}
    4 ?4 n) k3 P5 j" Z# v3 l
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则