PDO预处理防注入必须同时满足:使用占位符、禁用模拟预处理(ATTR_EMULATE_PREPARES=false)、值参数化(不可拼接表名/字段名),并配合白名单校验和异常模式。
pdo::prepare() 必须配合 bindValue() 或 execute() 才生效
很多人以为只要写了
就算防注入了,其实不然。如果只是
,那还是字符串拼接,
完全没起作用。
真正起效的链条是:写带占位符的 SQL → 调
→ 再用
、
或
传值。三者缺一不可。
位置占位符(
)必须按顺序绑定,
中的
是索引,不是数组键
命名占位符(如
)可用
,但键名必须严格匹配,多一个空格都不行
别在
里拼变量,哪怕你只拼了一个
,整条语句就退化为不安全状态
PDO::ATTR_EMULATE_PREPARES 必须设为 false
PHP 默认开启模拟预处理(
),这意味着 PDO 自己在 PHP 层做字符串替换,而不是交由 MySQL 服务端真正编译预处理语句。某些字符集组合下(比如连接未设
),攻击者能绕过防护。
正确初始化方式:
立即学习
“
PHP免费学习笔记(深入)
”;
不设
,
可能形同虚设,尤其在旧版 MySQL 或宽字节环境(如 GBK)中风险极高
不是可选,它让错误暴露出来,避免静默失败掩盖问题
连接 DSN 中建议显式指定字符集:
表名、字段名、ORDER BY 不能参数化,只能白名单校验
只保护“值”(WHERE 条件值、INSERT 的列值等),不保护语法结构。所以
或
永远不能靠占位符解决。
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
必须手动白名单控制:
字段名校验:
,注意第三个参数
启用严格类型检查
排序方向只接受
或
,且需转大写:
表名若需动态,必须从预定义列表中取:
数字参数不加引号,但依然必须用占位符
常见错误是认为 “数字不用加引号,所以可以拼进去”,比如:
。这看似安全,但漏掉了两个关键点:一是类型转换可能失败(如传入
会截成
),二是逻辑上开了例外口子,容易蔓延到其他地方。
正确做法是统一走占位符 + 显式类型绑定:
整数用
:
浮点数用
更稳妥(因
在部分驱动中行为不一致)
别依赖自动类型推断,
传的是字符串,PDO 可能不转类型,导致隐式转换或索引失效
最易被忽略的是:白名单校验和
这两处,一旦漏掉,整个预处理防线就存在可利用缺口。
prepare()$pdo->prepare("SELECT * FROM user WHERE name = '{$_POST['name']}'")prepare()prepare()bindValue()bindParam()execute()?bindValue(1, $val, PDO::PARAM_STR)1:nameexecute(['name' => $val])prepare()$tablePDO::ATTR_EMULATE_PREPARES => trueutf8mb4$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
falseprepare()PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTIONmysql:host=localhost;dbname=test;charset=utf8mb4prepare()"SELECT * FROM {$table}""ORDER BY {$sort_field} {$order_dir}"in_array($sort, ['id', 'title', 'created_at'], true)true'ASC''DESC'strtoupper($dir) === 'ASC' ? 'ASC' : 'DESC'$tables = ['users', 'posts', 'comments']; if (!in_array($table, $tables)) die('Invalid table');"WHERE id = " . (int)$_GET['id']id=1abc1PDO::PARAM_INT$stmt->bindValue(1, (int)$_GET['id'], PDO::PARAM_INT)PDO::PARAM_STRPDO::PARAM_DOUBLEexecute([$_GET['id']])PDO::ATTR_EMULATE_PREPARES => false