ThinkPHP防止路径遍历攻击必须采用realpath() + 前缀校验组合,仅靠正则约束、basename()或str_replace无效;需先urldecode()处理编码绕过,再白名单过滤文件名,最后确认真实路径落在授权目录内。

ThinkPHP路由正则约束本身不能直接防止路径遍历攻击,但它能从源头限制非法字符和结构,是防御链中关键的第一道过滤。真正防住路径遍历,必须配合后续的路径解析与校验,否则正则只是“画地为牢”,挡不住绕过。
正则约束要精准匹配,不是简单排除
很多人用->where(['file' => '.*'])或->where(['file' => '[^/\\]+']),这看似拦了斜杠,实则漏洞百出:攻击者可用%2e%2e%2f(URL编码)、.//、..\(Windows路径)、甚至Unicode归一化绕过。正则必须明确允许什么,而非模糊禁止什么。
- 文件名类参数(如
filename)应只允许安全字符:->where(['filename' => '[a-zA-Z0-9_.-]{1,64}']),长度和字符集双控 - ID类参数必须强类型:
->where(['id' => 'd+']),禁用d*(空值风险)和[0-9]+(PCRE兼容性差) - 路径片段(如
category)若需支持中文,应先URL解码再匹配:urldecode($value)后用/^[x{4e00}-x{9fa5}a-zA-Z0-9_-]+$/u
正则只是起点,realpath + 前缀校验才是核心
即使路由层用正则拦住了../,如果控制器里仍把input('get.file')拼进file_get_contents(),攻击者仍可能通过编码、空字节、符号链接等方式穿透。ThinkPHP 5.1+ 默认不处理路径穿越,所有input()返回原始字符串——你拼接时没校验,就等于交出服务器文件系统。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 先用
basename()剥离路径结构,再立即urldecode()处理编码绕过 - 对结果做白名单字符过滤:
preg_match('/^[a-zA-Z0-9_.-]+$/', $safe_name) - 构造完整路径后,必须调用
realpath()获取真实绝对路径,再用strpos($realpath, $allow_root) === 0确认是否落在授权目录内 - 示例:
$root = realpath('./public/files'); $real = realpath('./public/files/' . $safe_name); if (!$real || strpos($real, $root) !== 0) abort(403);
别依赖字符串替换或 basename 单独防御
str_replace(['..', './'], '', $input)或仅靠basename()根本不可靠。前者会被.//、%2e%2e%2f、....//绕过;后者对../../../etc/passwd%00.jpg只返回passwd%00.jpg,后续若用pathinfo($name, PATHINFO_EXTENSION)判断类型,%00后内容被忽略,扩展名校验失效。
立即学习“PHP免费学习笔记(深入)”;
- 所有路径类参数(
file、view、template)都视作高危输入,绝不直接拼接 - 上传/下载/删除场景,强制使用固定目录 + 随机哈希名(如
md5(uniqid() . $original)) - 禁用用户控制完整路径结构,业务逻辑中避免
include $_GET['page']这类裸露包含
路由层配合应用层做主动拦截
在路由解析后、控制器执行前加中间件,可提前识别可疑路径特征并阻断:
- 监听
Request::pathinfo(),对含../、php://、data://、双斜杠//、连续点号...的请求直接abort(403) - 用缓存标记记录IP频次,60秒内访问非白名单路径超15次,自动加入1小时黑名单
- 白名单路径硬编码(如
['/login', '/api/v1/user']),不依赖配置项,防覆盖或读取失败


















