ThinkPHP 3.1.2 SQL注入漏洞主因是where()方法误用字符串拼接和array('exp',...)等危险模式;修复应优先使用类型安全的数组条件、禁用exp表达式、采用%d/%s预处理占位符,并在基类中统一校验输入。

ThinkPHP 3.1.2 的 SQL 注入漏洞主要出现在 where() 方法使用不当、特别是混合数组与字符串条件、或误用 array('exp', ...) 类型时。框架虽内置类型强制转换和字符串转义,但某些边界场景(如 IN/BETWEEN 解析缺陷、正则匹配不严谨)仍可能被绕过。修复关键不是推倒重来,而是收紧输入解析、禁用危险模式、优先走安全路径。
优先用数组方式写查询条件
这是最直接有效的防御手段。ThinkPHP 对数组格式的 where 条件会自动做类型判断和转义,比如:
-
不安全写法:
$map = "id=".$_GET['id']." AND status='".$_GET['status']."'"; $Model->where($map)->select(); -
安全写法:
$map = array( 'id' => (int)$_GET['id'], 'status' => trim($_GET['status']) ); $Model->where($map)->select();
数值字段显式转 int/float,字符串字段用 trim 或 htmlspecialchars 过滤前后空格和特殊字符,避免进入字符串拼接分支。
禁用 array('exp', ...) 这类高危表达式
3.1.2 中 array('exp', '...') 是公认的注入入口,攻击者可构造 array('exp', "1=1 UNION SELECT password FROM admin") 绕过所有过滤。除非绝对必要且可控,否则应全局清除:
- 搜索项目中所有
array('exp'或['exp'出现的位置 - 替换成明确的数组键值对,例如把
'score' => array('exp', 'score > 60')改为'score' => array('gt', 60) - 若必须动态 SQL,改用预处理语法(见下一条)
字符串条件必须用预处理占位符
当确实无法避免字符串 where 条件时,ThinkPHP 3.1.2 已支持 %d / %s / %f 占位符预处理,数据库驱动层会将其转为原生 prepare 语句:
- 正确用法:
$Model->where("id=%d AND username='%s'", $_GET['id'], $_GET['username'])->select(); - 或传数组:
$Model->where("id=%d AND username='%s'", array($_GET['id'], $_GET['username']))->select(); - 注意:不要混用,例如
"id=%d AND name='{$name}'"中的{$name}仍是拼接,不安全
加固模型基类,统一拦截非法操作
在公共模型基类(如 Common/Model/BaseModel.class.php)中重写 where() 方法或添加前置校验:
- 检查传入的
$where是否为数组;如果不是,直接抛异常或记录告警 - 遍历数组值,若发现值是数组且首元素为
'exp'、'in'、'between'等关键字,且第二项含 SQL 关键字(如union、select、sleep),则拒绝执行 - 对所有外部输入字段,强制绑定字段类型,例如在模型里定义
protected $type = array('id'=>'integer', 'status'=>'string');
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











