ThinkPHP 8.0.4本身无原生文件读取入口,但开发者若直接使用file_get_contents等函数处理用户可控路径,易导致任意文件读取或RCE;需重点审计高危调用、实施白名单+真实路径校验+遍历字符过滤,并防范模板渲染等隐式风险。

ThinkPHP 8.0.4 本身不提供原生的“文件读取”功能入口,但开发者若在业务中直接使用 file_get_contents、readfile、include、require 或 file() 等函数处理用户可控路径,就极易触发任意文件读取(LFI)甚至远程代码执行(RCE)风险。审计重点不是框架有没有漏洞,而是你写的代码是否把用户输入当成了可信路径。
识别高危调用模式
逐行扫描控制器、中间件、服务类中所有涉及文件操作的代码,重点关注以下写法:
-
file_get_contents($_GET['file'])、readfile($_POST['path'])—— 输入未校验即传入读取函数 -
include 'templates/' . $_GET['tpl'] . '.php'—— 拼接路径且未限制目录范围 -
file($_SERVER['DOCUMENT_ROOT'] . '/uploads/' . input('filename'))—— 使用input()获取参数但未过滤../或空字节 - 使用
think\facade\Filesystem时传入未经白名单验证的$path,例如Storage::get($request->get('file'))
检查路径控制逻辑是否有效
仅做“后缀判断”或“黑名单过滤”是无效的。真正安全的路径控制必须满足三点:
- 使用 白名单机制:只允许读取预定义的几个文件名(如
['config.json', 'notice.txt']),拒绝一切未列明项 - 使用 真实路径解析 + 目录约束:通过
realpath()获取绝对路径,再用str_starts_with()判断是否在允许根目录内,例如:$real = realpath($userPath);<br>if (!$real || !str_starts_with($real, ROOT_PATH . 'public/static/')) { throw new HttpException(403); } - 禁用路径遍历字符:对输入强制替换或截断
..、/./、%2e%2e、\0等编码变体,不能只靠str_replace(['..', '%00'], '', $input)—— 它无法覆盖多层嵌套或双重编码
排查框架特性引入的隐式读取风险
ThinkPHP 自身在某些场景下会主动读取外部文件,需确认是否被用户输入影响:
立即学习“PHP免费学习笔记(深入)”;
-
模板渲染:若启用
fetch()并传入用户可控的模板名(如$this->fetch(input('tpl'))),且未限定模板目录,可能读取任意 PHP 文件(导致 RCE) -
语言包加载:
lang('user.' . input('key'))一般安全,但如果自定义了语言包路径且该路径由用户拼接,则存在风险 -
配置加载:避免在
config/目录外动态require用户指定文件,尤其不要响应?config=xxx类参数 -
日志/缓存路径:检查
config/cache.php和config/log.php中的path配置是否硬编码,而非由请求参数生成
验证与加固建议
完成代码审查后,用最小成本验证防护是否生效:
- 手工测试典型 payload:
?file=../../../../../etc/passwd、?file=php://filter/read=convert.base64-encode/resource=index.php、?file=data://text/plain,<?php phpinfo();?> - 在入口文件
public/index.php前加一层全局路径参数拦截(临时兜底):$danger_keys = ['file', 'path', 'tpl', 'view'];<br>foreach ($danger_keys as $k) {<br> if (isset($_GET[$k]) && preg_match('/\.\.|%00|php:\/\//i', $_GET[$k])) {<br> http_response_code(400);<br> exit('Invalid path');<br> }<br>} - 生产环境禁用危险协议:
ini_set('allow_url_fopen', '0');和ini_set('allow_url_include', '0');(写在public/index.php开头)



















