跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

PHP防止SQL注入_PDO预处理语句正确使用姿势【详解】

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

相关文章