文件上传漏洞防御,后端校验文件头、后缀、MIME绕过防护
文件上传漏洞的核心风险
很多网站在处理用户上传时,只靠前端限制扩展名,攻击者抓包改一下文件名和 Content-Type 就能绕过,把 PHP、JSP 等可执行脚本传上服务器。
一旦上传目录能执行脚本,网站基本就沦陷了。
所以服务端必须做多层校验,文件头、后缀、MIME 三者缺一不可。
本文用 PHP 示例演示后端如何实现,并给出验证和避坑方法。
准备条件
在动手前,准备好下面的环境:
- 一台可测试的服务器或本机环境,PHP 5.3 以上即可
- 一个简单的上传表单和一个上传处理接口
- Burp Suite 或浏览器开发者工具,用来模拟恶意请求
后端校验文件头、后缀和 MIME 的代码实现
下面以 PHP 为例,给上传接口加入三层校验。
注意代码顺序:先查后缀,再识别文件头,最后比对 MIME。
第一层:校验文件后缀
只允许白名单内的后缀,例如图片的 jpg、jpeg、png、gif。
$allowed = ['jpg', 'jpeg', 'png', 'gif'];
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) {
die('文件后缀不允许');
}
第二层:校验文件头
文件头是文件开头的几个字节,能反映真实格式。
JPEG 以 FF D8 FF 开头,PNG 以 89 50 4E 47 开头。
攻击者把 PHP 文件改成 .jpg 时,文件头通常不会变。
用 PHP 读取临时文件的前几个字节并判断:
$handle = fopen($_FILES['file']['tmp_name'], 'rb');
$header = fread($handle, 4);
fclose($handle);
if (!preg_match('/^(FF D8 FF|89 50 4E 47|47 49 46 38)/', bin2hex($header))) {
die('文件头不正确');
}
第三层:校验服务端识别的 MIME
不要使用 $_FILES['file']['type'],这个值来自客户端请求头,攻击者可以随意改成 image/jpeg。
要用 finfo_file 读取真实 MIME 并和扩展名比对:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
$map = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif'
];
if ($map[$ext] !== $mime) {
die('MIME 类型不匹配');
}
绕过方式与避坑清单
攻击者常见的绕过方法包括:
- 用
.php.jpg或.phtml等复合后缀,绕过只检查末尾扩展名的逻辑 - 在图片末尾追加脚本代码,配合文件包含漏洞执行
- 修改请求里的
Content-Type字段,绕过只校验 MIME 的代码 - 老版本系统里使用
shell.php%00.jpg空字节截断
避坑时记住这几条:
- 文件后缀和真实 MIME 必须一一对应,防止“图片后缀 + 脚本内容”的组合
- 上传目录关闭脚本执行权限,例如 Nginx 配置
location ~* \.(php)$ { deny all; } - 上传后文件名随机重命名,不用用户原始文件名,避免包含恶意字符串
- 安全修复完成后,还要定期扫描历史上传文件,防止旧文件已被植入后门
验证防御是否有效
用 Burp Suite 抓包,把一段 PHP 代码保存为 test.jpg,同时将请求里的 Content-Type 改成 image/jpeg,然后上传。
如果接口返回「文件头不正确」或「MIME 类型不匹配」,说明校验生效。
也可以直接上传一个内容为 的文件,改名为
php phpinfo(); ?
>.jpg,再访问上传路径,确认文件没有被当作 PHP 执行。
这样能同时验证后缀、文件头、MIME 和目录执行权限是否都起了作用。
常见疑问
为什么只校验后缀不够?
因为攻击者可以完全伪造后缀和请求头,文件内容才是真实身份。
检测文件头能完全防住图片马吗?
不能。图片马的文件头是正常的,需要配合上传目录禁止脚本执行、随机文件名和内容检测。
如果业务允许上传 PDF 或压缩包怎么办?
同样用 finfo_file 获取真实 MIME,维护一份更宽的白名单,并把文件放在独立存储目录,禁止执行脚本。
文件上传漏洞防御必须落在后端。只要把后缀白名单、
服务端文件头识别、
真实 MIME 比对三层校验都做到位,
再关闭上传目录的执行权限,
就能挡住绝大多数绕过攻击。 建议部署后按本文的验证方法多测几次,
确保上传接口是安全的。