thinkphp 5.0.13–5.0.15存在sql注入漏洞,因builder.php中parsedata()未过滤用户输入的数组值(如username[0]=exp),导致可控sql执行;官方已在5.0.16修复,建议升级至5.0.16或更高版本,或手动添加白名单校验及应用层参数类型约束。

ThinkPHP 5.0.13 至 5.0.15 版本中存在 SQL 注入漏洞,核心成因是 library/think/db/Builder.php 文件中的 parseData() 方法未对用户输入做严格过滤,直接将数组值(如 username[0]=exp&username[1]=...)拼接到 SQL 语句中,绕过常规过滤逻辑,导致可控 SQL 执行。
该漏洞无需登录、不依赖 debug 模式即可触发(但 debug 开启时更易利用报错回显),影响范围明确,修补需从版本升级和代码加固两方面入手。
升级到安全版本是最直接有效的修复方式
官方已在 5.0.16 版本中修复此问题,主要改动集中在 Builder.php 的 parseData() 和相关字段解析逻辑。建议操作如下:
- 确认当前版本:查看
thinkphp/library/think/App.php中的VERSION常量,或运行php think version - 升级命令(推荐 Composer):
composer update topthink/framework:^5.0.16
- 若无法升级,至少应升至 5.0.24 或更高(5.0.23 后已修复多处 RCE 和注入链)
- 注意:5.0.x 系列已于 2019 年停止官方维护,长期使用务必迁移到 6.x 或 8.x(LTS 支持中)
手动补丁:关键位置添加白名单校验
若暂不能升级,可在 parseData() 方法入口处增加字段值合法性检查。以 ThinkPHP 5.0.15 为例,在 library/think/db/Builder.php 的 parseData() 函数开头插入:
// 在 function parseData($data, $prefix = '') { 下方立即添加
foreach ($data as $key => $val) {
if (is_array($val) && isset($val[0]) && $val[0] === 'exp') {
// 拦截 exp 类型注入,仅允许预定义安全表达式
if (!in_array(strtolower((string)$val[1]), ['count(*)', 'max(id)', 'min(time)'], true)) {
throw new Exception('SQL expression not allowed');
}
}
// 阻止含 SQL 关键字或特殊符号的原始字符串值
if (is_string($val) && preg_match('/\b(select|union|insert|update|delete|drop|create|load_file|into\s+outfile)\b|[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]/i', $val)) {
throw new Exception('Suspicious SQL content detected');
}
}
⚠️ 注意:此补丁需同步检查
parseOrder()、parseField()、parseArrayData()等调用parseData()的方法是否同样被绕过;尤其order参数(如orderby[id|updatexml(...)%23]=1)在 5.1.x 中另有独立漏洞路径。
应用层防御:参数接收必须显式声明类型与长度
避免直接将 $_GET 或 input() 结果传入数据库操作。正确写法示例:
// ❌ 危险写法(直接透传)
$username = input('username/a'); // /a 表示自动解析数组,但无内容校验
db('user')->insert(['username' => $username]);
// ✅ 安全写法(强制类型+白名单+长度限制)
$username = input('username/s', '', 'htmlspecialchars'); // 字符串过滤
if (empty($username) || strlen($username) > 32 || !preg_match('/^[a-zA-Z0-9_\x{4e00}-\x{9fa5}]+$/u', $username)) {
throw new Exception('Invalid username format');
}
db('user')->insert(['username' => $username]);
同时禁用 input('xxx/a') 这类自动数组解析,改用 input('xxx.') 显式点号访问,或全部改用 Request::param() 并配合验证规则:
$data = $this->validate([
'username' => 'require|alphaNum|length:1,32',
'email' => 'email',
])->check(input('post.'));
不复杂但容易忽略
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











