必须用 prepare() + execute() 组合实现参数化查询,禁用模拟预处理并显式指定 charset=utf8mb4;命名占位符优先,like 查询需在 php 层添加通配符,id 和排序字段须白名单校验,表名字段名严禁用户输入。

必须用 prepare() + execute() 组合,不能只 new PDO
仅创建 PDO 实例($pdo = new PDO(...))不提供任何注入防护。真正起作用的是预处理流程:先调用 prepare() 传入带占位符的 SQL 模板,再用 execute() 或 bindParam() 传入用户数据。这样数据库才能把输入严格当作「值」而非「代码」处理。
错误示例:$pdo->query("SELECT * FROM users WHERE id = " . $_GET['id']);
这种字符串拼接在 PHP 8.4 中依然会触发宽字节注入、报错注入等风险。
连接时强制指定字符集并禁用模拟预处理
两个关键配置缺一不可:
- DSN 中显式声明 charset=utf8mb4,例如:
mysql:host=localhost;dbname=test;charset=utf8mb4 - 连接后立即关闭模拟预处理:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
若启用模拟预处理(默认为 true),PDO 会在 PHP 层自行转义,导致 LIKE、ORDER BY 等场景失效,攻击者可用 %df' OR 1=1 -- 类 payload 绕过防护。
正确使用占位符:命名式优先,顺序要匹配
推荐用命名占位符(如 :email),更清晰且不易出错:
- SQL 中写
WHERE email = :email AND status = :status - execute 时传关联数组:
['email' => $e, 'status' => $s] - 问号占位符(
?)要求严格按顺序传索引数组,混用会直接抛出PDOException: Invalid parameter number - LIKE 查询需在 PHP 层加通配符:
$keyword = '%' . trim($_GET['q']) . '%'; $stmt->execute([$keyword]);,不能写成WHERE name LIKE :q%
输入验证不能交给数据库兜底
参数化查询防的是语法注入,不是业务非法数据。必须额外校验:
- ID 类整数用
filter_var($id, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]),不用(int)强转 - 动态排序字段(如
ORDER BY :field)必须白名单校验:in_array($_GET['sort'] ?? '', ['created_at', 'name', 'email']) - 表名、字段名绝不能由用户输入直接代入,必须硬编码或从预设列表中选取
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











