必须同时用预处理语句重写所有数据库操作并在入口层对$_get、$_post、$_cookie做白名单校验;仅升级框架或仅过滤全局变量均无法彻底修复sql注入漏洞。

直接结论:只升级框架或只过滤全局变量,都不足以修复SQL注入漏洞;必须同时做两件事——用预处理语句重写所有数据库操作,并在入口层对 $_GET、$_POST、$_COOKIE 做白名单式校验。
为什么单纯升级框架不保险
很多CMS(如 PbootCMS V3.2.9、JizhiCMS 1.6.7)虽在新版本中加强了 frparam() 或 format_param() 过滤,但实际审计发现:这些函数仍依赖 magic_quotes_gpc 等过时机制,且未覆盖所有控制器调用路径。例如 ParserController.php 中的 parserSearchLabel() 方法,绕过点就在标签解析阶段,和框架主过滤逻辑是脱节的。
升级后仍需人工验证以下场景:
- 所有
M()->find()、M()->where()调用是否都改用了参数占位符(如where('htmlurl = ?', $url)) - 模板标签(如
{pboot:if})内嵌的 PHP 表达式是否被禁用或沙箱隔离 - 备份恢复类功能(如
dorestore_action())是否仍直接拼接用户传入的目录名并执行file_get_contents()
为什么全局变量过滤不能代替预处理
像 addslashes()、htmlspecialchars()、正则替换 select|union|from 这类“关键词黑名单”方式,已被证实可被宽字节、十六进制编码(0x73656c656374)、注释符绕过。JizhiCMS 的 format_param($value, 1) 就存在此问题——它只对字符串做 addslashes(),但没阻止攻击者把恶意 SQL 塞进整型参数位置。
真正有效的过滤策略是:
- 对所有入口参数强制声明类型:
$id = (int)$_GET['id']或$slug = preg_replace('/[^a-z0-9\-]/', '', $_GET['slug']) - 拒绝任何含非预期字符的输入,不尝试“修复”,直接
exit或返回 400 - 禁用
eval()、create_function()、assert()等动态执行函数,CMS 模板引擎若支持 PHP 代码执行,必须关闭
必须重写的三类高危代码模式
以下代码在 PbootCMS、JizhiCMS、phpcms2008 等系统中反复出现,不改必留后门:
-
$res = M('user')->find(array('username' => $_GET['u']));→ 改为$res = M('user')->where('username = ?', $_GET['u'])->find(); -
$sql = "SELECT * FROM {$table} WHERE id = {$_GET['id']}"; mysqli_query($conn, $sql);→ 改为$stmt = $conn->prepare("SELECT * FROM ? WHERE id = ?"); $stmt->bind_param("si", $table, $_GET['id']);(注意:表名不能参数化,需白名单校验) -
include template('phpcms', $_GET['tpl']);→ 改为$allowed_tpls = ['list', 'detail', 'search']; $tpl = in_array($_GET['tpl'], $allowed_tpls) ? $_GET['tpl'] : 'list'; include template('phpcms', $tpl);
最易被忽略的是缓存与文件操作——type.php 中的 extract()、dorestore_action() 中的 front::get('db_dir'),它们不走数据库,却能触发任意 SQL 执行。这类逻辑必须剥离用户输入,或加签名验证。











