PDO场景下的SQL注入探究
字数 1660 2025-08-26 22:11:14
PDO场景下的SQL注入探究
1. PDO概述
PHP数据对象(PDO)扩展为PHP访问数据库定义了一个轻量级的一致接口。PDO提供了一个数据访问抽象层,这意味着不管使用哪种数据库,都可以使用相同的函数(方法)来查询和获取数据。
PDO特性:
- 随PHP 5.1发行
- 在PHP 5.0的PECL扩展中也可以使用
- 无法运行于PHP 5.0之前的版本
2. PHP连接MySQL的三种方式比较
| 特性 | Mysqli | PDO | MySQL |
|---|---|---|---|
| 引入的PHP版本 | 5.0 | 5.0 | 3.0之前 |
| PHP5.x是否包含 | 是 | 是 | 是 |
| 服务端prepare支持 | 是 | 是 | 否 |
| 客户端prepare支持 | 否 | 是 | 否 |
| 存储过程支持 | 是 | 是 | 否 |
| 多语句执行支持 | 是 | 大多数 | 否 |
3. PDO多语句执行与堆叠注入
3.1 PDO默认支持多语句查询
PDO默认情况下支持多语句查询,这可能导致堆叠注入(SQL堆叠查询)漏洞。
示例代码:
$dbms = 'mysql';
$host = '192.168.27.61';
$dbName = 'test';
$user = 'root';
$pass = 'root';
$dsn = "$dbms:host=$host;dbname=$dbName";
try {
$pdo = new PDO($dsn, $user, $pass);
} catch (PDOException $e) {
echo $e;
}
$id = $_GET['id'];
$sql = "SELECT * from user where id =" . $id;
$stmt = $pdo->query($sql);
攻击者可构造如下URL进行攻击:
http://example.com/test.php?id=1;create table aaa(id int)
3.2 防范措施
禁止多语句执行,可在创建PDO实例时将PDO::MYSQL_ATTR_MULTI_STATEMENTS设置为false:
new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => false))
4. MySQL预处理机制
MySQL预处理语句的SQL语法基于三个SQL语句:
prepare stmt_name from preparable_stmt;execute stmt_name [using @var_name [, @var_name] ...];{deallocate | drop} prepare stmt_name;
5. PDO预处理模式
PDO分为两种预处理模式:
5.1 模拟预处理(默认)
- 通过设置
PDO::ATTR_EMULATE_PREPARES为true启用(默认值) - PDO内部模拟参数绑定过程
- SQL语句在最后execute()时才发送给数据库执行
5.2 非模拟预处理
- 通过设置
PDO::ATTR_EMULATE_PREPARES为false启用 - 分两步执行:
- prepare阶段:发送SQL语句模板到数据库服务器
- execute阶段:发送占位符参数给数据库服务器
6. 预处理下的安全问题
6.1 模拟预处理下的注入
当SQL语句中存在动态拼接的部分时,仍可能存在注入风险:
$username = $_GET['username'];
$sql = "select id," . $_GET['field'] . " from user where username = ?";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(1, $username);
$stmt->execute();
攻击者可构造field参数进行多语句执行或报错注入。
6.2 非模拟预处理下的注入
虽然非模拟预处理下多语句不可执行,但当设置PDO::ATTR_ERRMODE为PDO::ERRMODE_EXCEPTION时,仍可能进行报错注入。
这是因为MySQL服务端prepare时报错,然后通过PDO设置将错误信息打印出来。
7. 安全建议
-
尽量使用非模拟预处理:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); -
禁止多语句查询:
new PDO($dsn, $user, $pass, array(PDO::MYSQL_ATTR_MULTI_STATEMENTS => false)) -
避免动态拼接SQL语句模板:
- 不要将用户输入直接拼接到SQL语句中
- 即使是预处理语句,也要确保所有可变部分都通过参数绑定
-
合理设置错误模式:
- 生产环境中避免显示详细错误信息
- 开发时可使用
PDO::ERRMODE_EXCEPTION便于调试
8. 总结
PDO虽然提供了预处理等安全机制,但不当使用仍可能导致SQL注入漏洞。开发者应当:
- 理解PDO的工作原理和不同模式的区别
- 正确配置PDO的安全参数
- 避免任何形式的SQL语句动态拼接
- 根据实际需求选择合适的错误报告级别
通过合理配置和安全编码实践,可以充分发挥PDO的安全优势,有效防范SQL注入攻击。