跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

ThinkPHP如何获取上传文件的原始文件名_Security过滤与处理

应弃用原始文件名,改用md5_file($file->getRealPath()).'.'.$file->guessExtension()生成唯一安全文件名,并按日期分层存储;原始名仅可经htmlspecialchars后存数据库。 ThinkPHP 获取上传文件原始文件名的正确方式 直接用
$file->getInfo('name')
拿到的是客户端提交的原始文件名,但这个值不可信——浏览器可伪造、含路径、带编码、甚至含恶意字符。ThinkPHP 默认会做基础过滤(如去除路径),但不会解码或标准化,所以不能直接用于存储或展示。
$file->getOriginalName()
是更安全的选择,它内部调用
mb_convert_encoding
尝试还原原始编码,并剥离路径,但依然不保证 100% 可读(比如 IE 上传的 GBK 文件名可能乱码) 若需强一致性,建议在前端用
encodeURIComponent
编码文件名,后端用
urldecode
+
mb_convert_encoding
手动处理 永远不要把原始文件名拼进 SQL 或直接输出到 HTML,否则可能触发 XSS 或路径遍历 ThinkPHP 文件上传时的 Security 过滤默认行为 ThinkPHP 6 的
File
类在实例化时会自动调用
filterFilename
方法,对原始文件名做两件事:一是用正则
/[/\:*?"\|]/
剔除 Windows 路径与非法字符;二是用
basename()
截取最后一段。但它 不会 过滤空字节、Unicode 控制字符、或同形字(如全角冒号)。 默认不校验文件扩展名是否与 MIME 匹配,
$file->getMime()
可被伪造,必须配合
getimagesize()
或
finfo_open()
实际检测内容 上传目录权限必须设为非 Web 可执行(如不放
.php
解析规则),且禁止解析用户上传的任意后缀
validate(['size'=>1024*1024, 'ext'=>'jpg,png,gif'])
中的
ext
是白名单校验,但仅检查后缀,不是最终防线 绕过 ThinkPHP 安全过滤的常见漏洞场景 攻击者常利用开发者忽略的边界情况绕过防护,比如上传
shell.php.jpg
(后缀合法但实际是 PHP)、或
xxx..\..shell.php
(双点+反斜杠绕过 basename)。ThinkPHP 的
filterFilename
对后者能拦截,但对前者无感。 Windows 下用
..\
或
../
组合可能绕过部分旧版过滤逻辑(TP 6.0.13+ 已加固) 上传文件名含
%00
(空字节)会导致
basename()
截断,后续字符串操作失效(PHP 7.4+ 已修复,但低版本仍存在) 使用
$file->move($path, $name)
时若
$name
来自用户输入(如表单传的 name 字段),完全跳过框架过滤,极其危险 生产环境推荐的文件名生成与存储方案 别依赖原始文件名做任何关键逻辑。真实项目中应彻底弃用它,改用服务端可控的命名策略。 PHP 8.5.5 PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。 下载 立即学习 “ PHP免费学习笔记(深入) ”; 文件名用
md5_file($file->getRealPath()) . '.' . $file->guessExtension()
生成,确保唯一且可校验内容 存储路径按日期分层,如
uploads/2024/06/15/xxx.jpg
,避免单目录海量文件 如果业务强制需要保留原始语义(如合同上传),可将原始名经
htmlspecialchars()
后存入数据库字段,而非文件系统 开启
upload_max_filesize
和
post_max_size
限制,并在 Nginx/Apache 层加
client_max_body_size
,防大文件耗尽内存 最易被忽略的一点:ThinkPHP 的
validate
规则只在校验阶段生效,一旦
move()
开始执行,就不再检查。所以所有安全逻辑必须在 move 前完成,包括 MIME 检测、内容扫描、病毒查杀等。

相关文章