文件上传MIME类型校验,绕过上传漏洞防御
文件上传功能如果只依赖前端或简单的MIME类型校验,很容易被绕过并上传恶意脚本。
本文从零基础视角出发,讲清MIME校验的绕过原理,并给出Nginx、PHP环境下的防御配置和验证步骤,帮你堵住上传漏洞。
绕过是怎么发生的
很多网站只检查浏览器提交的 Content-Type,比如只允许 image/jpeg。
攻击者用Burp Suite等工具抓包,把 Content-Type 改成 image/jpeg,文件内容却是PHP代码,服务器若只信这个头就会放行。
另一种常见绕过是修改文件扩展名,
比如上传 shell.php.jpg,
如果后端只检查扩展名后缀,
可能被解析成PHP。结论:
仅靠客户端或单一MIME头校验无法防御上传漏洞,
必须在服务端做多重校验。
防御配置:Nginx与PHP配合
1. 限制上传目录执行权限
在Nginx站点配置中,找到上传目录的 location 块,添加以下内容禁止执行PHP:
location ~* /uploads/.*\.(php|php5|phtml|jsp|asp|aspx)$ {
deny all;
}
如果上传目录不需要执行任何脚本,可以直接关闭PHP解析:
location ^~ /uploads/ {
location ~ \.php$ {
deny all;
}
}
修改后执行 nginx -t 测试配置,然后 systemctl reload nginx 重载。
2. PHP端严格校验MIME和扩展名
在接收上传的PHP脚本中,不要只用 $_FILES['file']['type'],而要用 finfo_file() 读取真实MIME:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$realMime = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
$allowMime = ['image/jpeg', 'image/png', 'image/gif'];
$allowExt = ['jpg', 'jpeg', 'png', 'gif'];
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($realMime, $allowMime) || !in_array($ext, $allowExt)) {
exit('文件类型不允许');
}
关键点:finfo_file() 读取文件内容判断类型,比浏览器提交的MIME可靠得多。
3. 重命名上传文件
保存时用随机文件名加白名单扩展名,避免用户控制路径和文件名:
$newName = bin2hex(random_bytes(16)) . '.' . $ext;
move_uploaded_file($_FILES['file']['tmp_name'], '/var/www/uploads/' . $newName);
避坑指南
- 不要相信
$_FILES['file']['type']:这个值来自客户端,可随意伪造。 - 不要只检查扩展名:攻击者可能上传
.php.jpg或利用解析漏洞。 - 上传目录不要给执行权限:即使文件被上传,也无法运行。
- 宝塔面板用户:在“网站”->“设置”->“配置文件”中修改Nginx规则,或在“PHP设置”中禁用危险函数如
system、exec。 - 验证时用真实攻击测试:上传一个内容为
但MIME为image/jpeg的文件,确认被拦截。
验证防御是否生效
- 用浏览器正常上传一张JPG图片,应成功。
- 用Burp Suite抓包,把
Content-Type改为image/jpeg,文件内容改为PHP代码,上传应返回“文件类型不允许”。 - 直接访问上传目录下的PHP文件,应返回403或404。
- 检查Nginx错误日志
tail -f /var/log/nginx/error.log,确认拦截记录。
如果以上步骤都通过,说明MIME类型校验和绕过防御已基本到位。
常见疑问
问:用了 finfo_file() 就绝对安全吗?
答:不是。还需配合白名单扩展名、重命名、目录权限控制和Web应用防火墙。
问:Nginx deny all 会影响图片访问吗?
答:只拦截指定脚本扩展名,图片等静态文件不受影响。
问:宝塔面板如何快速禁用上传目录PHP执行?
答:在网站设置->配置文件里,在 location ~ \.php$ 前添加 location ^~ /uploads/ { deny all; } 并重载Nginx。
防御文件上传漏洞没有一劳永逸的方法,但做好MIME类型校验、扩展名白名单、目录权限隔离这三层,就能挡住绝大多数绕过手段。
建议先在测试环境验证,再应用到生产服务器。