ThinkPHP 漏洞(下)

一、紧急临时修复(未升级前先用,止损优先)

适用于无法立即升级框架版本的场景,核心是拦截危险请求、过滤敏感参数、禁用高危功能

1. 入口文件加固(public/index.php)

define('APP_PATH', __DIR__ . '/../app/');之后添加以下代码,拦截 RCE/SQL 注入的核心攻击参数:

php 复制代码
<?php
// 1. 禁用危险函数调用(按需添加)
if (!function_exists('disable_danger_functions')) {
    function disable_danger_functions() {
        $danger_functions = ['eval', 'system', 'exec', 'passthru', 'shell_exec', 'proc_open', 'popen'];
        foreach ($danger_functions as $func) {
            if (function_exists($func)) {
                rename_function($func, '_' . $func); // 重命名危险函数(需安装runkit扩展)
            }
        }
    }
    // 仅生产环境启用
    if (env('app_debug') === false) {
        disable_danger_functions();
    }
}

// 2. 过滤危险请求参数
$danger_params = [
    's', '_method', 'function', 'vars', 'filter', 'order', 'group', 'having',
    '\\think\\', 'call_user_func', 'call_user_func_array'
];
// 检查GET/POST/COOKIE参数
$request_data = array_merge($_GET, $_POST, $_COOKIE);
foreach ($request_data as $key => $value) {
    if (in_array($key, $danger_params) || is_string($value) && strpos($value, '\\think\\') !== false) {
        http_response_code(403);
        die('Illegal Request: ' . $key);
    }
}

// 3. 限制请求方法(仅允许必要的)
$allow_methods = ['GET', 'POST', 'PUT', 'DELETE'];
if (!in_array($_SERVER['REQUEST_METHOD'], $allow_methods)) {
    http_response_code(405);
    die('Method Not Allowed');
}
?>

2. 禁用自动路由与调试模式

修改config/app.php,关闭高风险默认配置:

php 复制代码
return [
    // 禁用自动路由(防止未授权访问)
    'auto_route' => false,
    // 生产环境必须关闭调试模式(防止信息泄露)
    'app_debug' => false,
    'app_trace' => false,
    // 禁用远程调试
    'remote_debug' => false,
    // 限制日志级别(不输出敏感信息)
    'log_level' => ['error', 'warning'],
];

3. Nginx/Apache 层面拦截

在 Web 服务器配置中添加规则,拦截恶意请求:

nginx

复制代码
# Nginx配置(nginx.conf / 站点配置)
server {
    # 拦截包含ThinkPHP RCE特征的请求
    if ($request_uri ~* "(\\/think\\/|call_user_func|eval\\(|system\\(|exec\\()") {
        return 403;
    }
    # 拦截SQL注入关键词
    if ($request_uri ~* "(union select|insert into|update |delete from|drop table|-- |#)") {
        return 403;
    }
    # 禁止访问敏感目录
    location ~ ^/(runtime|config|vendor|.git)/ {
        deny all;
    }
    # 禁止访问PHP配置文件
    location ~ \.(php|php5|php7)$ {
        try_files $uri =404;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

二、版本根治修复(唯一彻底解决漏洞的方式)

不同版本的升级路径明确,优先升级到对应分支的最新稳定版:

ThinkPHP 版本 存在漏洞版本范围 升级目标版本 升级注意事项
3.2.x 全版本 建议直接升级到 6.x 3.2.x 已停止维护,无安全更新,需重构部分代码(如控制器、模型写法)
5.0.x 5.0.0-5.0.23 5.0.24+ 仅需替换框架核心文件(thinkphp / 目录),代码兼容度 99%
5.1.x 5.1.0-5.1.37 5.1.38+ / 6.1.x 5.1.x 升级到 6.x 需适配命名空间、中间件写法
6.0.x 6.0.0-6.0.12 6.0.13+ / 6.1.x 6.x 升级仅需执行composer update topthink/framework
8.0.x 8.0.0-8.0.x(如有漏洞) 8.0.x 最新稳定版 8.x 为最新分支,通过 composer 自动更新即可

升级操作步骤(以 6.x 为例):

复制代码
# 1. 备份项目代码和数据库
# 2. 进入项目根目录,执行composer升级
composer update topthink/framework --no-dev
# 3. 清理缓存
php think clear
# 4. 验证升级成功
php think version

三、分类型漏洞专项修复

针对不同类型的漏洞,给出精准的修复代码和配置:

1. RCE 漏洞修复(核心)

  • 根本:升级框架版本;

  • 代码层面 :禁止直接调用危险函数,统一使用框架封装的方法:

    php 复制代码
    // 错误:直接使用call_user_func
    call_user_func($_GET['func'], $_GET['param']);
    // 正确:白名单限制可调用函数
    $allow_functions = ['getUser', 'getOrder', 'getGoods'];
    $func = input('get.func/s');
    if (in_array($func, $allow_functions) && function_exists($func)) {
        $func(input('get.param/d'));
    }

2. SQL 注入漏洞修复

核心原则:所有 SQL 操作必须使用参数绑定,禁止手动拼接

php 复制代码
// 错误写法(拼接变量)
$id = input('get.id');
$user = Db::table('user')->where("id = $id")->find();

// 正确写法1:数组条件
$user = Db::table('user')->where(['id' => $id])->find();

// 正确写法2:占位符绑定
$user = Db::table('user')->where("id = :id", ['id' => $id])->find();

// 针对order/group注入:白名单限制
$allow_sort = ['id', 'name', 'create_time'];
$sort = input('get.sort/s', 'id');
$sort = in_array($sort, $allow_sort) ? $sort : 'id';
$list = Db::table('goods')->order($sort)->select();

3. 任意文件操作修复

(1)文件读取 / 下载
php 复制代码
// 错误:直接使用用户传入的文件路径
$file = input('get.file');
return download($file);

// 正确:白名单+真实路径校验
$allow_files = [
    realpath(ROOT_PATH . 'public/static/download/guide.pdf'),
    realpath(ROOT_PATH . 'public/static/download/readme.txt')
];
$file = realpath(input('get.file')); // 解析真实路径,防止../遍历
if (!in_array($file, $allow_files) || strpos($file, ROOT_PATH) === false) {
    die('Illegal File');
}
return download($file);
(2)文件上传
php 复制代码
// 上传验证:后缀+MIME+内容三重校验
$file = request()->file('upload');
// 1. 后缀白名单
$validate = new \think\file\Validate([
    'ext' => 'jpg,png,pdf', // 仅允许这些后缀
    'size' => 1024*1024*2, // 限制2MB
]);
if (!$file->validate($validate)->check()) {
    return json(['code' => 0, 'msg' => $file->getError()]);
}
// 2. MIME类型校验
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file->getRealPath());
$allow_mime = ['image/jpeg', 'image/png', 'application/pdf'];
if (!in_array($mime, $allow_mime)) {
    return json(['code' => 0, 'msg' => '文件类型非法']);
}
// 3. 重命名文件,防止覆盖
$save_name = \think\facade\Filesystem::disk('public')->putFile('upload', $file, 'md5');

4. 未授权访问 / 权限绕过修复

(1)全局权限中间件(ThinkPHP6+)

创建app/middleware/Auth.php

php 复制代码
<?php
namespace app\middleware;

class Auth
{
    public function handle($request, \Closure $next)
    {
        // 白名单路由(无需登录)
        $white_list = ['/login', '/register', '/api/captcha', '/static/*'];
        $current_path = $request->path();
        
        // 检查白名单
        $is_white = false;
        foreach ($white_list as $white) {
            if ($white === $current_path || (strpos($white, '*') !== false && strpos($current_path, rtrim($white, '*')) === 0)) {
                $is_white = true;
                break;
            }
        }
        if ($is_white) {
            return $next($request);
        }
        
        // 检查登录状态
        if (!session('user_id')) {
            // AJAX请求返回JSON,普通请求重定向
            if ($request->isAjax()) {
                return json(['code' => 401, 'msg' => '未登录']);
            } else {
                return redirect('/login');
            }
        }
        
        // 检查角色权限(按需添加)
        $role = session('role');
        $admin_routes = ['/admin/*', '/api/admin/*'];
        if (in_array($role, ['admin']) === false && $this->matchRoute($current_path, $admin_routes)) {
            return json(['code' => 403, 'msg' => '无权限']);
        }
        
        return $next($request);
    }
    
    // 路由匹配辅助方法
    private function matchRoute($path, $routes)
    {
        foreach ($routes as $route) {
            if (strpos($route, '*') !== false && strpos($path, rtrim($route, '*')) === 0) {
                return true;
            } elseif ($route === $path) {
                return true;
            }
        }
        return false;
    }
}

app/middleware.php中注册:

php 复制代码
return [
    \app\middleware\Auth::class, // 全局生效
];
(2)禁用空控制器

修改config/route.php

php 复制代码
return [
    'empty_controller' => '', // 禁用空控制器,防止绕过
    'route_check' => true, // 开启路由校验
];

四、全维度长期加固(生产环境必做)

1. 配置层面

  • 数据库配置加密:使用环境变量 / 配置中心存储数据库账号密码,不直接写在database.php中;

    php 复制代码
    // config/database.php
    return [
        'username' => env('DB_USERNAME', ''),
        'password' => env('DB_PASSWORD', ''),
    ];
  • 禁用函数:在php.ini中配置disable_functions = eval,system,exec,passthru,shell_exec,proc_open,popen

  • 限制 PHP 执行权限:open_basedir = /var/www/your-project/:/tmp/,防止跨目录访问。

2. 代码层面

  • 统一使用input()方法获取参数,指定类型过滤:

    php 复制代码
    $id = input('get.id/d', 0); // 强制转为数字
    $name = input('post.name/s', '', 'trim,htmlspecialchars'); // 过滤HTML字符
  • 日志脱敏:对日志中的密码、Token 等敏感信息进行替换;

  • 避免使用$_GET/$_POST/$_COOKIE全局变量,统一用request()对象。

3. 部署层面

  • 启用 HTTPS,配置 HSTS,防止中间人攻击;
  • 部署 WAF(如阿里云 WAF、宝塔 WAF),拦截 SQL 注入、XSS、RCE 等攻击;
  • 定期备份代码和数据库,防止漏洞被利用后数据丢失;
  • 以非 root 用户运行 PHP-FPM/Nginx,限制文件读写权限。

总结

  1. ThinkPHP 漏洞修复的核心优先级:先升级框架版本(根治)→ 紧急拦截危险请求(止损)→ 分类型修复专项漏洞(精准)→ 全维度加固(长期);
  2. 所有修复的底层逻辑:禁用高危功能 + 白名单校验输入 + 最小权限运行,尤其是禁用调试模式、强制参数绑定、配置全局权限中间件这三点;
  3. 临时修复仅用于应急,长期必须升级到最新稳定版,同时定期扫描依赖漏洞(如使用composer audit)。
相关推荐
千里马学框架21 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台21 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone21 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui