flawopen.com/PHP 中的 SQL Injection/mysqli 与 PDO
MySQLi 与 PDO:安全使用预处理语句
参考页面
简答
预处理语句固定 SQL 指令,并单独发送输入值,就像填写带标签的空格。PDO 和 MySQLi 都能做到。安全性取决于是否正确绑定值,而非库的名称。
同一查询在两种 API 中的安全写法
不安全 — SQL 拼入请求数据
$userId = $_GET['id']; $sql = "SELECT id, email FROM users WHERE id = " . $userId; // Both calls execute the already-built SQL string: $rows = $mysqli->query($sql); $rows = $pdo->query($sql);
安全 — 两种 API 均绑定值
// PDO $stmt = $pdo->prepare( "SELECT id, email FROM users WHERE id = :id" ); $stmt->execute(["id" => $userId]); // MySQLi $stmt = $mysqli->prepare( "SELECT id, email FROM users WHERE id = ?" ); $stmt->bind_param("i", $userId); $stmt->execute();
数据库可移植性是 PDO 的主要区别
数据库可移植性是 PDO 的主要区别
PDO 为多个数据库驱动提供统一接口;MySQLi 面向 MySQL 和 MariaDB。PDO 本身不会让 SQL 自动可移植:方言、驱动行为和功能仍有差异。
占位符写法有所不同
根据驱动,PDO 可使用 :id 命名占位符或 ? 位置占位符;不要混用。MySQLi 使用位置 ?。两者都绑定值,不能绑定表名、列名、关键字或排序方向。
PDO 预处理模式取决于驱动
PDO 驱动可能使用原生或模拟预处理。对 MySQL,PDO::ATTR_EMULATE_PREPARES 在支持时控制模拟;原生预处理不可用或失败时,驱动可能模拟或回退。应遵循驱动文档。此设置不会保护插值 SQL:无论模式如何,都应固定查询文本并绑定值。
两种库都适用的 SQL Injection 防护规则
使用 prepare() 并绑定每个不可信值。保持 SQL 语法固定;动态标识符应从严格的服务端映射中选择,或构造有限的查询变体。预处理语句不负责授权行访问;必要时按已认证用户限定查询。对于 LIKE,绑定可防止 SQL 语法注入,但若未转义为字面搜索,% 和 _ 仍是通配符。
如何选择 PDO 或 MySQLi
- ✓需要跨驱动统一 API 时选 PDO;迁移时核对 SQL 方言和驱动行为。
- ✓仅使用 MySQL/MariaDB 且需要其专用 API 时选 MySQLi。
- ✓无论选择哪种,都检查插值、拼接和动态标识符;绑定值,并单独授权记录访问。
常见问题
PDO 命名占位符比 MySQLi 的问号更安全吗?
不能。只要查询经过预处理且值被绑定,两者都能安全绑定值。命名占位符可读性更好,但不能绑定 SQL 标识符,也不能替代授权。
应该始终禁用 PDO 模拟预处理吗?
不应一概而论。驱动支持和回退行为各异。查阅驱动文档和应用需求。任何模式下都要固定 SQL 结构并将值作为参数传入。
预处理语句能绑定表名或列名吗?
不能。绑定参数代表值,不能代表标识符或 SQL 关键字。应在服务端将客户端选项映射到有限的可信 SQL 片段。