php 8 中防止 sql 注入唯一可靠方式是 pdo::prepare + 参数绑定并禁用模拟预处理(pdo::attr_emulate_prepares => false),所有用户输入(含数字、布尔值、like 通配符)必须进占位符,表名、字段名、order by 等结构化部分须白名单校验。

PHP 8 中防止 SQL 注入,唯一可靠方式是用 PDO::prepare + 参数绑定,其他任何字符串拼接、filter_var 过滤、或旧式 mysql_real_escape_string 都不防得住——尤其在数字字段、宽字节、或动态排序场景下会直接失效。
必须禁用 PDO 模拟预处理(PDO::ATTR_EMULATE_PREPARES => false)
PHP 8 默认仍开启模拟预处理(emulate_prepares = true),这意味着 PDO::prepare 实际只是在 PHP 层做字符串替换,根本没走 MySQL 的服务端预编译,绕过它就能触发注入。
- 显式关闭:连接时传入选项
PDO::ATTR_EMULATE_PREPARES => false - 若 MySQL 服务端不支持(如极老版本),会抛出异常,这是好事——说明你得升级数据库,而不是退回去用模拟
- 不设这个选项,
bindValue或execute(['id' => '1 OR 1=1'])依然可能被解析成逻辑运算
所有用户输入都得进占位符,包括数字和布尔值
常见错误是“只对字符串加引号,数字就直接拼”:"WHERE id = " . $_GET['id'] 即使加了 (int) 强转,遇到 id=1%00 或特殊编码仍可能被绕过;而预处理能彻底隔离类型。
- 整型参数也必须用占位符:
$stmt = $pdo->prepare("SELECT * FROM posts WHERE id = ? AND status = ?") - 绑定时指定类型更稳妥:
$stmt->bindValue(2, (int)$_GET['status'], PDO::PARAM_INT) - 别信“
(int)就安全”——如果后续逻辑里又把这整数拼进另一个 SQL(比如日志表名),照样崩
LIKE 查询的通配符必须在 PHP 层加,不能塞进占位符右边
WHERE name LIKE ? 绑定 '%admin%' 是安全的;但写成 WHERE name LIKE ?% 或 "%{$_GET['q']}%" 拼接,就等于把控制权交还给用户输入。
- 正确做法:
$search = '%' . str_replace(['%', '_'], ['\%', '\_'], $_GET['q']) . '%';(先转义再加通配符) - 然后
$stmt->execute([$search]) - MySQLi 同理:
$stmt->bind_param("s", $search),不要试图在 SQL 字符串里写LIKE ? ESCAPE '\'然后让参数带反斜杠——escape 规则易错且难维护
表名、字段名、ORDER BY 方向无法绑定,必须白名单校验
预处理语句只保护“值”,不保护“结构”。ORDER BY ? 或 FROM ? 语法错误,PHP 会直接报 SQLSTATE[HY093]。
- 排序字段只允许固定几个:
$sort = in_array($_GET['sort'], ['created_at', 'title', 'score']) ? $_GET['sort'] : 'created_at'; - 方向只认
ASC/DESC:$dir = strtoupper($_GET['dir']) === 'DESC' ? 'DESC' : 'ASC'; - 拼接进 SQL 时不做任何转义,只靠白名单兜底:
"ORDER BY $sort $dir"
真正容易被忽略的不是“怎么写 prepare”,而是“哪些地方根本不能用 prepare”——字段名、UNION 表名、LIMIT 的偏移量(虽是数字但常被用于盲注)、甚至 JSON_EXTRACT 路径里的键名。这些全得靠白名单+严格验证,没捷径。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











