本质是服务端未严格校验文件真实类型,仅依赖客户端传来的扩展名或简单字符串匹配;加固核心是剥离原始文件名、验证文件头(magic bytes)、禁用上传目录脚本执行权限,并将文件存于web根目录外非公开路径。

ThinkPHP6 文件上传后缀校验被绕过,本质是服务端未严格校验文件真实类型,仅依赖客户端传来的扩展名或简单字符串匹配。加固核心不是加更多后缀白名单,而是剥离文件名、验证内容、独立存储路径。
剥离原始文件名,禁用用户可控的扩展名
不要直接使用 $_FILES['file']['name'] 中的完整文件名,尤其不能用 pathinfo($name, PATHINFO_EXTENSION) 做唯一判断。攻击者可构造 shell.php.jpg 或 xxx.jpg/.php 绕过。
- 统一生成安全文件名,如
md5(uniqid() . mt_rand()) . '.jpg' - 从原始文件名中只提取扩展名用于后续 MIME 和内容校验,不用于保存
- 若业务必须保留原始名称,应单独存入数据库字段,与实际存储路径解耦
强制校验文件头(Magic Bytes),不信任 MIME 类型
浏览器提交的 Content-Type 可任意伪造,finfo_file() 检查文件头才是可靠方式。
- 使用 PHP 的
finfo_open(FILEINFO_MIME_TYPE)获取真实类型 - 对图片类上传,限定返回值必须为
image/jpeg、image/png等明确值,拒绝text/plain、application/octet-stream - 避免只检查是否包含
image/字符串,防止image/xxx-php类绕过
限制上传目录权限与解析行为
即使文件名和内容都“安全”,Web 服务器错误解析仍会导致执行风险。
- 上传目录禁止执行脚本:Nginx 配置中添加
location ~ \.(php|html|js|css)$ { deny all; } - Apache 下设置
Options -ExecCGI并禁用AddHandler对上传目录生效 - 物理存放路径不要放在 Web 根目录下,建议存到
/data/uploads/等非 Web 可访问位置,通过代理或下载接口提供访问
启用 ThinkPHP6 内置安全配置,不依赖默认
TP6 的 validate 方法支持后缀、MIME、大小等多维校验,但需显式开启并补全逻辑。
- 在验证规则中明确写死允许的扩展名,如
'ext' => 'jpg,png,gif',不要留空或用通配符 - 配合
'mimeType' => 'image/jpeg,image/png,image/gif'双重校验 - 设置
'size' => 2097152(2MB)等合理上限,防大文件耗尽资源 - 关键:校验通过后,务必调用
$file->move()而非copy()或手动写入,确保框架内部安全处理流程生效
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











