ThinkPHP缓存漏洞是系统性风险,核心在于用户输入未经净化写入可执行.php文件,攻击者利用换行符%0a绕过//注释触发RCE;影响3.2.x、5.0.0–5.0.10及部分6.x版本,关键路径为Cache::set()→serialize()→写入文件;需立即清空runtime目录、关闭调试、拦截高危参数,并禁用用户输入直传缓存与动态包含。

ThinkPHP缓存漏洞不是个别现象,而是长期存在于多个版本中的系统性风险。核心问题在于:缓存数据被写入可执行的 .php 文件,且未对用户输入做有效净化,导致攻击者能注入换行符绕过注释、写入恶意代码,最终触发远程代码执行(RCE)。
缓存漏洞怎么发生的
漏洞多见于 File 缓存驱动(默认配置),关键路径是 Cache::set() → serialize() → 写入 .php 文件。虽然框架在序列化内容前加了 // 注释头,但攻击者用 %0a(换行符)即可跳到下一行,使后续 PHP 代码生效。
- ThinkPHP 5.0.0–5.0.10:缓存键可控 + 换行注入 → 写入 webshell 到 runtime/cache/ 目录
- ThinkPHP 3.2.x:S() 函数直接缓存用户输入,
S('name', I('post.a3'))配合换行可落地一句话木马 - ThinkPHP 6.x:虽改用更安全的存储格式,但若业务层仍用
include cache_file.php且路径拼接未校验,仍可能触发文件包含 RCE
三步快速止血(10分钟内必须完成)
发现被黑或扫描告警后,立即执行以下操作,切断当前攻击链:
- 清空
runtime/cache/和runtime/log/全部文件——缓存文件一旦生成就可能已被利用,日志里可能藏有 shell 路径 - 关闭调试模式:
config/app.php中确认'app_debug' => false、'show_error_msg' => false,并重启服务 - 在入口文件
public/index.php的define('APP_PATH', ...)后插入拦截逻辑,对请求参数含eval、assert、call_user_func、$$、%0a的一律http_response_code(403); exit;
业务代码必须清理的两类高危写法
框架升级不能代替代码治理。97% 的缓存类 RCE 来自业务层主动“邀请”攻击者写文件:
立即学习“PHP免费学习笔记(深入)”;
-
禁止将用户输入直传 Cache::set() 或 S():如
Cache::set('user_data', input('post.data'));应先过滤、白名单校验、或改用 JSON 格式缓存(file_put_contents($path, json_encode($data))) -
禁用动态包含缓存文件:如
include 'runtime/cache/' . $_GET['v'] . '.php';必须改为固定路径 + 白名单判断,或彻底弃用 include,改用Cache::get()安全读取
长效防御机制
避免“修一次、漏三次”,要建立可持续运行的安全习惯:
- 上线前强制检查:
runtime/目录权限设为755,且禁止 Web 用户直接访问(Nginx/Apache 配置location ~ ^/runtime/ { deny all; }) - 缓存驱动升级:TP5+ 项目建议切换至 Redis 或 Memcached,规避文件写入风险;若必须用 File,启用
'data_compress' => true并在set()前手动过滤换行符:str_replace(["\r", "\n"], '', $value) - CI/CD 流程固化:每次构建加入脚本,扫描代码中是否出现
Cache::set、S(、include.*cache等关键词,命中即阻断发布



















