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

PHP 8.3 中防止 SQL 注入,唯一可靠路径是 PDO::prepare + 参数绑定 + 显式禁用模拟预处理(PDO::ATTR_EMULATE_PREPARES => false)。任何字符串拼接、类型强转、filter_var 或旧式转义函数都挡不住宽字节、数字字段绕过、LIKE 通配符注入等真实攻击场景。
必须关闭 PDO 模拟预处理
PHP 8.3 默认仍启用 PDO::ATTR_EMULATE_PREPARES => true,这意味着 prepare() 只在 PHP 层做字符串替换,根本没走 MySQL 服务端预编译。攻击者传入 id=1%00 OR 1=1 或 status=0; DROP TABLE users; 仍可能触发逻辑执行。
- 连接时必须显式传入选项:
new PDO($dsn, $user, $pass, [PDO::ATTR_EMULATE_PREPARES => false]) - 若 MySQL 服务端版本低于 5.1.17(极罕见),会抛出
PDOException——这不是 bug,是强制你升级数据库的信号 - 不设该选项,哪怕用了
bindValue(1, $_GET['id'], PDO::PARAM_INT),也无法阻止服务端解析阶段的注入
所有用户输入都得进占位符,包括整数和布尔值
“数字不用防”是最大误区。用 (int)$_GET['id'] 强转后拼进 SQL:"WHERE id = " . (int)$_GET['id'],遇到 URL 编码 %00 或 UTF-8 变体仍可能被 MySQL 宽字节截断绕过;而预处理能彻底隔离数据类型与语法上下文。
- 正确写法:
$stmt = $pdo->prepare("SELECT * FROM orders WHERE user_id = ? AND paid = ?") - 绑定时指定类型更稳妥:
$stmt->bindValue(2, (bool)$_GET['paid'], PDO::PARAM_BOOL) - 命名占位符可读性更好:
WHERE status = :status,但底层安全机制与问号无异
LIKE 查询的通配符必须在 PHP 层加,不能塞进 SQL 字符串
WHERE name LIKE ?% 或 "%{$_GET['q']}%" 是典型错误——占位符只包裹值,不控制其在 SQL 中的位置或语义。用户传入 q=admin%27 OR %271%27=%271 就会直接拼进查询。
- 先清理通配符特殊字符:
$q = str_replace(['%', '_'], ['\%', '\_'], $_GET['q'] ?? '') - 再手动加前后缀:
$search = '%' . $q . '%' - 最后绑定:
$stmt->execute([$search]),且需在 SQL 中声明 ESCAPE:WHERE name LIKE ? ESCAPE '\' - 别用
bind_param("s", "%$q%")—— MySQLi 的bind_param不支持在参数值里带格式符号
表名、字段名、ORDER BY 等结构化部分无法绑定,必须白名单校验
ORDER BY ? 会报错 SQLSTATE[HY093],因为预处理只保护值,不保护语法结构。攻击者通过 sort=id; DROP TABLE logs; 或 sort=score DESC, (SELECT password FROM users LIMIT 1) 可直接注入。
- 只允许固定字段:
$allowed_sort = ['created_at', 'title', 'score']; $sort = in_array($_GET['sort'] ?? '', $allowed_sort) ? $_GET['sort'] : 'created_at'; - 动态表名同理:
$table = in_array($_GET['type'], ['posts', 'comments']) ? $_GET['type'] : 'posts'; - 不要试图用
preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $input)做“宽松校验”——正则无法覆盖所有服务端解析歧义
最易被忽略的是:字符集必须设为 utf8mb4(不是 utf8),否则宽字节注入仍可能发生;还有,PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION 必须开启,否则绑定失败时静默吞掉错误,导致逻辑误判。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











