回答

收藏

如何防止 PHP 中的 SQL 注入?

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

如果用户输入未经修改就插入 SQL 在查询中,应用程序很容易受到影响SQL 注射攻击,如下例所示:5 N- l8 M0 X' ?& O# ]9 e7 B. k8 M
    $unsafe_variable = $_POST['user_input']; mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");" D" T4 x9 a2 l5 o8 F, Q4 J
因为用户可以输入相似之处value'); DROP TABLE table;--查询的内容变为:& B" ?0 s3 |% f) |: j! u
    INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
    9 z% a) H3 J( x6 `3 x# D) N0 ~" T6 g
能做些什么来防止这种情况发生?2 d! [" j! ]$ a; E7 B0 T
                                                                & J. i# B# I4 q: X' i6 Z
    解决方案:                                                               
! h9 m, U" ]6 n% D! u                                                                无论使用哪个数据库,都要避免 SQL 注入攻击正确方法是将数据与 SQL 分离,保持数据,永远不会被 SQL 解析器解释命令。可以使用正确格式的数据部分创建 SQL 句子,但如果你不是完全了解细节,要始终如一使用准备好的句子和参数查询。这些都是与任何参数分开发送到数据库服务器并由其分析的 SQL 语句。这样,攻击者就不可能注入恶意 SQL。/ O2 l4 r, S- c2 r' K7 ~
实现这一点基本上有两种选择:0 Z/ e/ }* h( G. r, g0 Z$ @2 w2 w
[ol]使用PDO(任何支持的数据库驱动程序):[/ol]```php
' H; e5 K" D" W8 D; v                $stmt = $pdo->prepare(‘SELECT * FROM employees WHERE name = :name’);
9 m& R! t. {' L$stmt->execute([ 'name' => $name ]);foreach ($stmt as $row)      / Do something with $row}```
4 x3 \2 G& D* y/ S  {[ol]使用MySQLi(用于 MySQL):[/ol]```php
8 U4 d" A. t8 A2 o                $stmt = $dbConnection->prepare(‘SELECT * FROM employees WHERE name = ?’);0 k; Z( M+ G$ e# U9 V, M; Z3 }
                $stmt->bind_param(‘s’,$name); // ‘s’ specifies the variable type => ‘string’/ P, |5 t5 F4 x# b, ?$ G) a# H
$stmt->execute();$result = $stmt->get_result();while ($row = $result->fetch_assoc()Do something with $row}```
0 K' Y: |0 ]# o2 L$ z  B若要连接 MySQL 以外的数据库可参考驱动程序的第二个选项(例如,pg_prepare()对于pg_execute()PostgreSQL)。PDO 是通用选项。8 F8 R: g% o/ _! J  j7 _
连接设置正确使用时请注意PDO*访问 MySQL 数据库时**,默认情况下不使用实际准备好的句子。要解决这个问题,你必须禁止模拟准备好的句子。使用PDO*创建连接的例子是:$ B  O5 s; G, d
    $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);/ W) @3 U/ H! P2 w2 u' H6 h
在上面的示例中,错误模式不是绝对必要的,但建议添加。这样脚本就不会了Fatal Error当出现问题时停止。它给了开发人员处理问题的机会。n as s 的catch任何错误。throw``PDOException  m2 d7 y- e8 M
然而,第一行是强制性setAttribute(),它告诉 PDO 禁用模拟预准备句,使用模拟预准备句真正的准备句子。这确保了句子和值发送到 MySQL 服务器以前不会被  PHP 分析(使可能的攻击者没有机会注入恶意 SQL)。+ J  ]# M4 |7 r$ a- B  K6 I+ N
尽管您可以charset 设置在构造函数的选项中,但重要的是版 很重要PHP(5.3.在6 之前)默默忽略了 DSN 中的 charset 参数。
- ?/ s; g. h/ n, |2 i' ?解释你传递给你SQL 语句prepare由数据库服务器进行分析和编译。指定参数(如上例中的参数或命名参数:name),您可以告诉数据库引擎您要过滤的位置。然后,当您呼叫 时execute,准备好的句子与您指定的参数值相结合。
, r. d8 I8 m; u' U$ \, a参数值与编译句子相结合,而不是 SQL 字符串。SQL 注入的工作原理是创建要发送到数据库的 SQL 诱导脚本包含恶意字符串。因此,通过实际 SQL 单独发送参数,你可以限制结束你不想要的风险。
! s$ c8 z7 B7 I8 A1 w/ D0 f您在使用准备好的句子时发送的任何参数都将被视为字符串(尽管数据库引擎可能会进行一些优化,但参数最终可能以数字形式结束)。述示例中,如果$name变量包含'Sarah'; DELETE FROM employees结果只是对 string 的搜索"'Sarah'; DELETE FROM employees",你最终不会得到一个空 table。
. _. W% a1 n0 Z7 w+ A9 V使用准备句子的另一个好处是,如果你在同一个会话中多次执行相同的句子,它只会被分析和编译一次,从而给你带来一些速度提高。5 u1 I) J2 O/ _4 \) s" }% P9 y
哦,既然你问过如何插入,这里有一个例子(使用 PDO):/ r) C6 f. Z# {. {! q: |* ^

    & J- G5 u3 P& a- Q4 o4 e
  • $preparedStatement = $db->prepare('INSERT INTO table (column) VALUES (:column)');$preparedStatement->execute([ 'column' => $unsafeValue);(//);code]准备好的句子能用于动态查询吗?虽然您仍然可以使用准备好的语句来查询参数,但动态查询本身的结构不能参数化,某些查询功能也不能参数化。+ f) ?# D  Y, |" g" u
  • 对于这些特定场景,最好的办法是使用限制可能值的白名单过滤器。[code]// Value whitelist// $dir can only be 'DESC',otherwise it will be 'ASC'if (empty($dir) || $dir !== 'DESC {   $dir = 'ASC';}
    4 E0 Q4 n; n4 Z0 B; F4 |0 G
分享到:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则