php 8.0+ 彻底移除 mysql_* 系列函数,仅支持 pdo 和 mysqli 预处理防注入;必须禁用模拟预处理(pdo::attr_emulate_prepares => false)、正确绑定参数、白名单校验非值位置(如表名、order by 字段),否则防护失效。

PHP 8.0+ 已彻底移除 mysql_* 系列函数,PDO 和 mysqli 是唯一可用且受支持的预处理方案;只要用对了绑定方式、关掉了模拟预处理、没在占位符位置硬塞表名字段名,就能防住 99% 的 SQL 注入。
PDO 必须关掉 PDO::ATTR_EMULATE_PREPARES
PHP 8 默认仍可能启用模拟预处理(即 PHP 自己做字符串替换),这会让 prepare() 形同虚设。不关它,等于写了防注入代码却没生效。
- 连接时必须显式设置:
PDO::ATTR_EMULATE_PREPARES => false - 同时建议开启异常模式:
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,否则绑定失败或类型错配可能静默失败 - 执行前可验证是否真走原生预处理:
var_dump($pdo->getAttribute(PDO::ATTR_EMULATE_PREPARES));输出false才算到位
mysqli 中 bind_param() 的引用陷阱
mysqli_stmt::bind_param() 要求所有变量都传引用,直接传值会报 Number of variables doesn't match number of parameters,但错误信息不直观,容易卡在调试环节。
- 类型字符串(如
"ss")字符数必须和后续变量个数完全一致,少一个或多一个都失败 - 变量必须是已声明的变量名,不能是表达式:
$stmt->bind_param("s", $_POST['email'] . '@test.com')是非法的 - 整数参数别只靠
(int)强转,要配合类型字符"i",否则传0或负数可能被截断为0
哪些地方根本不能用占位符?
ORDER BY 字段、GROUP BY 列、表名、数据库名、ASC/DESC 排序方向——这些属于 SQL 语法结构,不是“值”,预处理机制根本不支持绑定。
- 必须用白名单校验:
in_array($sort, ['id', 'name', 'created_at'], true) - 拼接前确保变量是白名单里的纯字母字符串,不能含点、反引号、空格等
- 像
LIMIT ?, ?是合法的,但LIMIT ? OFFSET ?中的OFFSET关键字不能参数化,只能写死或白名单控制
最常被忽略的是:哪怕用了预处理,只要在 SQL 字符串里混入任何用户输入(比如 "SELECT * FROM {$table} WHERE ..."),整个防护就崩了。防注入不是“用了 prepare 就万事大吉”,而是“所有外部输入,要么进占位符,要么过白名单”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











