thinkphp5.0.13–5.0.15存在sql注入漏洞,源于parsedata()对['exp', '...']数组结构未校验字段名,导致upper(1)等表达式直拼sql;须升级至5.0.25+或5.1.40+,禁用/a修饰符,严格白名单过滤字段,禁用用户可控的exp/inc/dec操作。

ThinkPHP5.0批量添加(insert)时的SQL注入风险,核心在于parseData()函数对数组型参数的不当解析——当用户传入形如['username' => ['exp', 'UPPER(1)']]的结构时,框架会直接将UPPER(1)拼入SQL,绕过参数绑定。这不是配置疏忽,而是5.0.13–5.0.15等版本中设计缺陷导致的底层漏洞。
严格校验插入数据来源
所有来自用户端的数据(GET、POST、路由参数、JSON Body)必须视为不可信输入,禁止未经清洗直接进Db::table()->insert()或Model::create()。
- 使用
input()时指定类型并过滤:例如input('data.username/s')强制字符串、input('data.age/d')强制整数,避免数组绕过 - 禁用
/a(数组接收)修饰符,除非业务明确需要且后续做了白名单字段校验 - 对批量插入的数组,先用
array_filter()和in_array()确认键名在预设字段白名单中,如['username','email','status']
避免手动构造含exp/inc/dec的动态数据
exp(表达式)、inc(自增)、dec(自减)三类操作在insert()或update()中若由用户控制,极易触发注入。官方已在5.0.16起加了字段名校验,但旧版本仍需规避。
- 不要接受用户传来的
['field' => ['exp', '...']]结构;如需动态SQL逻辑,改用闭包查询+whereRaw()并手动绑定参数 - 批量插入固定字段值时,全部使用标量(字符串、数字),杜绝嵌套数组
- 若必须用
setInc()等方法,确保字段名硬编码,不从请求中读取
升级或打补丁是根本解法
该漏洞影响范围明确:5.0.13 ≤ ThinkPHP5 ≤ 5.0.15及部分5.1.x早期版本。官方已在5.0.16修复parseData()中对exp分支的字段名比对逻辑。
- 生产环境立即升级至
ThinkPHP5.0.25+或5.1.40+(长期支持版本) - 若无法升级,可临时重写
think\db\Builder::parseData(),在case 'exp':分支前加入if (!in_array($key, $fields)) { throw new Exception('Invalid field'); } - 关闭
app_debug = false,防止错误信息泄露数据库结构和账号密码
用事务+预处理替代裸insert(推荐方案)
即使数据可信,大批量插入也建议走预处理语句,既防注入又提性能。
- 用
Db::query()配合?占位符:例如Db::query("INSERT INTO user (name,age) VALUES (?,?)", [$name, $age]) - 批量插入时,拼接多个
(?,?)占位符,再把所有值扁平化为一维数组传入 - 搭配
Db::startTrans()与Db::commit(),保证原子性,失败自动回滚
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











