pdo连接必须禁用模拟预处理(setattribute(pdo::attr_emulate_prepares, false))、开启异常模式、dsn指定charset=utf8mb4;绑定用execute()传数组而非bindparam();禁止混用占位符;整型需(int)转换;like的%须php拼接;动态字段如order by必须白名单校验。

PDO连接时必须禁用模拟预处理
默认情况下,PDO在MySQL驱动中启用PDO::ATTR_EMULATE_PREPARES(模拟预处理),这意味着SQL模板和参数都在PHP层拼接后再发给数据库——这会绕过真正的预处理机制,让prepare()形同虚设,宽字节或特殊编码仍可能触发注入。
- 务必在连接后立即关闭模拟:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false) - 同时开启异常模式:
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION),避免错误被静默吞掉 - DSN中明确指定字符集:
mysql:host=localhost;dbname=test;charset=utf8mb4,防止因字符集不匹配导致的宽字节注入
占位符绑定必须用execute()传数组,别用bindParam()搞错顺序
两种绑定方式本质不同:bindParam()是引用绑定,变量值在execute()时才读取;而execute()直接传数组更直观、不易出错,尤其适合单次查询。
- 推荐写法:
$stmt->execute([':name' => $_POST['username'], ':status' => (int)$_POST['status']]) - 避免混用命名占位符和问号:
SELECT * FROM users WHERE id = ? AND name = :name——PDO不支持混合占位符 - 整型参数仍需显式转换:
(int)$_GET['id'],因为PDO::PARAM_INT不会自动转类型,只影响发送到MySQL的类型声明
LIKE查询里的百分号必须在PHP里拼,不能塞进占位符
占位符只接受完整值,LIKE '%?%'或LIKE :keyword中的%会被当作字面量,查不出任何结果;更糟的是,如果用户输入%admin%再拼进去,就等于放行了通配符滥用。
- 正确做法:
$search = '%' . $pdo->quote($_POST['keyword']) . '%';或更安全的:$search = '%' . str_replace(['%', '_'], ['\%', '\_'], $_POST['keyword']) . '%'; - 然后绑定:
$stmt->execute([$search]),确保%是PHP生成、非用户可控 - 若需转义下划线
_,记得在SQL里加ESCAPE '\',例如:WHERE name LIKE ? ESCAPE '\'
动态字段(如ORDER BY)无法用占位符,必须白名单校验
SQL语法结构部分(列名、表名、排序方向、GROUP BY字段)不能参数化——占位符只适用于数据值,数据库不允许把ORDER BY ?里的?当列名解析。
- 只能靠白名单硬控制:
$order = in_array($_GET['sort'], ['created_at', 'price', 'name']) ? $_GET['sort'] : 'created_at'; - 排序方向同样限制:
$dir = strtoupper($_GET['dir']) === 'DESC' ? 'DESC' : 'ASC'; - 拼接时不做任何转义,直接嵌入:
"ORDER BY {$order} {$dir}"——前提是变量已严格落在白名单内
真正危险的从来不是参数怎么绑,而是哪些地方根本绑不了。动态SQL片段一旦漏掉白名单,前面所有预处理都白做。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











