必须升级PHP 8.2.35不能修复SQL注入漏洞,因其仅修复CVE-2026-109618和CVE-2026-109619两个RCE漏洞;SQL注入属应用层逻辑缺陷,需改代码,如禁用$_GET直接拼接、改用预处理语句或严格过滤。

必须升级到 PHP 8.2.35,但仅升级不解决 SQL 注入漏洞。PHP 8.2.35 修复的是 CVE-2026-109618 和 CVE-2026-109619 这两个远程代码执行漏洞,和 $_GET 拼接 SQL 完全无关。你遇到的是应用层逻辑缺陷,得改代码,不是换 PHP 版本就能绕过去的。
mysqli_query() 直接拼接 $_GET 的典型错误写法
这类代码在老系统里极常见:
$id = $_GET['id']; $sql = "SELECT * FROM users WHERE id = " . $id; $result = mysqli_query($conn, $sql);
问题在于:mysqli_query() 本身不校验、不转义、不参数化——它只负责把字符串扔给 MySQL 执行。攻击者传 id=1 OR 1=1 就能拖库。
- 别信
addslashes()或mysql_real_escape_string():宽字节、多编码、二次注入都能绕过 - 别用
(int)强转应付所有场景:name=abc' OR '1'='1这类字符型参数会直接失效 - 别在 SQL 字符串里拼表名、字段名、
ORDER BY或LIMIT:预处理语句不支持这些位置占位
数字型参数(如 id、page)的最小安全改造
如果确认该参数只应为整数,且业务上不允许为空或负值,可先做类型强控再拼接:
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
立即学习“PHP免费学习笔记(深入)”;
$id = filter_var($_GET['id'], FILTER_VALIDATE_INT);
if ($id === false || $id < 1) {
die('invalid id');
}
$sql = "SELECT * FROM users WHERE id = " . $id;
-
filter_var($id, FILTER_VALIDATE_INT)比(int)更严格:它返回false而不是 0,能区分id=abc和id=0 - 必须显式判断返回值是否为
false,不能只用if (!$id),否则id=0会被误杀 - 这个方案只适用于纯数字上下文;一旦字段是
VARCHAR类型(比如username),立刻失效
字符型参数必须用 mysqli_prepare() + bind_param()
这是唯一被 MySQL 官方推荐、能彻底阻断注入的方案:
$name = $_GET['name'] ?? ''; $stmt = mysqli_prepare($conn, "SELECT * FROM users WHERE name = ?"); mysqli_stmt_bind_param($stmt, "s", $name); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt);
-
"s"表示字符串类型;数字用"i",布尔用"i",大文本用"s"即可,不用区分TEXT/MEDIUMTEXT - SQL 模板中只允许
?占位符,不能写成"WHERE name = '$name'"或"WHERE name = ? AND status = ?"以外的任何形式 - 不要试图“封装一个通用 query 函数”来自动判断类型——类型必须由开发者在每处调用时明确指定
ORDER BY 和 LIMIT 子句无法参数化,只能白名单+范围限制
例如:sort=title&limit=20 这种 URL 参数,不能塞进 ?,必须硬校验:
$allowed_sorts = ['title', 'created_at', 'updated_at'];
$sort = $_GET['sort'] ?? 'created_at';
if (!in_array($sort, $allowed_sorts)) {
$sort = 'created_at';
}
$limit = (int)($_GET['limit'] ?? 20);
$limit = min(max($limit, 10), 100); // 限定 10–100 条
$sql = "SELECT * FROM posts ORDER BY {$sort} DESC LIMIT {$limit}";
-
ORDER BY后面拼字段名是高危动作,白名单是唯一可靠方式;正则过滤(如/^[a-z_]+$/)可能漏掉带下划线的合法字段,也可能被 Unicode 绕过 -
LIMIT的值虽可用?(MySQL 支持,PostgreSQL 不支持),但旧驱动兼容性差,统一用(int)+ 范围限制更稳妥 - 别忽略
GROUP BY、HAVING、UNION SELECT等同样无法参数化的子句,它们都需要独立白名单机制
最常被忽略的一点:CMS 模板标签(如 {pboot:list filter="id={$_GET['id']}"} )本质也是拼 SQL,但开发者常以为“在模板里就不是代码”。只要最终落到 mysqli_query(),它就和裸写 PHP 一样危险。所有模板解析入口,必须走同套类型校验或预处理逻辑,不能另起炉灶。


















