ThinkPHP 3.2.4 SQL注入漏洞主因是parseOrder方法仅校验ASC/DESC而忽略括号、注释符等SQL元字符,导致updatexml()等恶意payload绕过检测;修复关键为order参数白名单校验、禁用动态字符串传参、加固数组参数处理及关闭调试模式。

ThinkPHP 3.2.4 的 SQL 注入漏洞主要集中在 order、where、find、select、delete 等方法对数组参数的解析环节,攻击者可通过构造恶意数组(如 id[where]=... 或 order=updatexml(...))绕过默认过滤,直接拼接进 SQL 语句。修复不依赖升级框架,关键是在保留业务逻辑的前提下,切断非法输入进入查询构造流程的路径。
堵住 order 方法注入入口
漏洞根因在 ./ThinkPHP/Library/Think/Db/Driver.class.php 的 parseOrder 方法:仅简单判断是否含 ASC/DESC,却未校验字段名合法性,导致可插入注释符(/*,*/)、括号等绕过字符。
- 在调用
order()前强制白名单校验:只允许字母、数字、下划线、点号,且必须匹配预设字段列表,例如:$validFields = ['id', 'name', 'create_time', 'status'];<br>$order = I('order');<br>if (!in_array($order, $validFields) && !preg_match('/^('.implode('|', $validFields).')\s+(ASC|DESC)$/i', $order)) {<br> exit('Invalid order parameter');<br>} - 禁用动态传参方式,改用固定选项映射:
$map = ['1' => 'id DESC', '2' => 'name ASC'];<br>$order = $map[I('sort')] ?: 'id DESC';<br>$res = M('user')->order($order)->select();
加固 where 和 find 类方法的数组参数处理
当 find()、select() 接收数组参数(如 I('GET.id') 返回数组),会跳过主键校验,直接进入 _parseOptions,而其中 table、alias、where 字段未经任何过滤即拼入 SQL 模板。
- 禁止将
I()结果直接传给模型方法:把$data = M('user')->find(I('GET.id'));改为显式提取并类型校验:$id = I('GET.id', '', 'intval'); // 强制转整型<br>if (empty($id)) { $this->error('Invalid ID'); }<br>$data = M('user')->find($id); - 对必须支持数组条件的场景(如多条件搜索),手动构建
where数组,拒绝原始输入:$where = [];<br>if ($name = I('name')) { $where['name'] = ['like', '%'.htmlspecialchars($name).'%']; }<br>if ($status = I('status', 0, 'intval')) { $where['status'] = $status; }<br>$list = M('user')->where($where)->select();
关闭调试模式并清理危险配置
调试模式开启时(SHOW_ERROR_MSG=true)会暴露完整 SQL 和数据库结构,极大降低攻击门槛;同时部分老项目残留的宽松过滤配置(如 DEFAULT_FILTER = '')也会削弱防护。
- 确认
ThinkPHP/Conf/convention.php中以下配置为安全值:'SHOW_ERROR_MSG' => false,<br>'DB_SQL_LOG' => false,<br>'LOG_RECORD' => false, // 或至少设为 'ERR'
- 检查
Application/Common/Conf/config.php或模块级配置中是否覆盖了全局过滤规则,确保'DEFAULT_FILTER' => 'htmlspecialchars'生效,且未被设为空或危险函数(如stripslashes) - 删除
Runtime/Logs/目录下所有日志文件,防止历史 SQL 泄露(尤其含敏感字段的查询)
补充:模板与数据库连接层加固
虽非 SQL 注入主因,但模板包含(fetch()、display())和数据库配置硬编码也是常见风险点,应一并收紧。
- 限制模板加载路径:在控制器基类中重写
fetch(),校验模板名仅允许字母、数字、下划线、斜杠,且不得含../或以.开头:if (!preg_match('/^[a-zA-Z0-9_\/]+$/', $template) || strpos($template, '../') !== false || $template[0] === '.') {<br> E('Invalid template path');<br>} - 数据库配置从代码中剥离:将
DB_HOST、DB_NAME等移至/data/conf/database.php(Web 不可访问目录),并在convention.php中通过require加载
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











