PHP上传图片校验MIME类型防止伪装文件
时间:2026-11-14 07:19 作者:emer 分类: PHP
前言
只靠前端扩展名判断上传文件,攻击者可以把 PHP 改成 .jpg 上传并执行。安全做法是校验真实 MIME 和文件头:getimagesize 判断是否真图片,finfo 识别真实类型。本文讲解两层校验、白名单扩展名、重命名存储、拒绝伪装文件的完整代码。
操作步骤
1. 用 getimagesize 判断是否真图片
$img = @getimagesize($_FILES['file']['tmp_name']);
if (!$img) {
exit('不是有效图片');
}
2. 用 finfo 校验真实 MIME
$finfo = new Finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['file']['tmp_name']);
$allow = ['image/jpeg','image/png','image/gif','image/webp'];
if (!in_array($mime, $allow)) {
exit('文件类型不允许');
}
3. 同时校验扩展名白名单
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
$allowExt = ['jpg','jpeg','png','gif','webp'];
if (!in_array($ext, $allowExt)) exit('扩展名不允许');
4. 重命名存储,避免文件名攻击
$newName = uniqid().'.'.$ext;
move_uploaded_file($_FILES['file']['tmp_name'], '/upload/'.$newName);
5. 限制大小
if ($_FILES['file']['size'] > 2*1024*1024) exit('图片过大');
常见坑
- 只校验前端传来的扩展名和 MIME,伪造极易绕过。
- getimagesize 对 SVG 可能误判,需单独处理。
- finfo 扩展未安装导致函数不存在,检查 php.ini。
- 上传目录设置可执行权限,伪装脚本被执行,目录关闭执行。
- 保留原始文件名,可能含路径穿越或特殊字符。
- 不同浏览器上报的 MIME 略有差异,以文件头为准。
- 大图未压缩直接存,占用空间过大。
总结
图片上传安全三层:getimagesize 验证真图片、finfo 验证文件头 MIME、扩展名白名单,再重命名存储。前端信息不可信,一切以临时文件的真实内容为准。上传目录禁止执行脚本,大小单独限制。
遇到问题加QQ23979811 协助处理