仅用$_FILES['type']校验MIME类型不可靠,因其源自客户端可控的Content-Type头;应使用finfo_file()基于文件二进制内容识别,并结合getimagesize()等函数做图像结构验证。

只用$_FILES['type']校验MIME类型为什么不行
因为 $_FILES['type'] 来自客户端 HTTP 请求头的 Content-Type 字段,攻击者用 Burp 或 curl 一改就变,比如把 image/jpeg 改成 text/plain 或 application/x-php,服务端照单全收。这不是检测,是信任不可信输入。
finfo_file() 是当前最可靠的MIME识别方式
它读取文件前几百字节的二进制内容(magic bytes),和系统内置的 MIME 数据库比对,结果基本无法伪造。使用时注意三点:
- 必须用
finfo_open(FILEINFO_MIME_TYPE),不能只用FILEINFO_MIME(后者返回带编码的完整字符串,如text/plain; charset=utf-8) - 路径要传
$_FILES['file']['tmp_name'],不是用户传来的原始文件名 - 调用前检查是否为有效临时文件:
is_uploaded_file($_FILES['file']['tmp_name'])
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, ['image/jpeg', 'image/png', 'image/gif'])) {
die('不支持的文件类型');
}
白名单必须覆盖真实业务需求,不能只写常见类型
比如允许 PDF 上传,就得明确加入 application/pdf;若需 Excel,得加 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet。但以下类型一律禁止:
-
application/x-php、text/x-php、application/x-httpd-php -
text/html、application/xhtml+xml -
application/x-sh、application/x-perl
注意:有些 PDF 生成器会输出 application/octet-stream,这属于漏报,需结合扩展名+内容双重判断,不能直接拒之门外。
立即学习“PHP免费学习笔记(深入)”;
图片文件还得额外做内容校验
仅靠 MIME 仍可能被“图片马”绕过——攻击者把 PHP 代码拼接在 JPEG 文件末尾,finfo_file() 仍返回 image/jpeg。此时应补充:
- 用
getimagesize()验证是否真能被 PHP 图像函数解析(失败则说明不是合法图片) - 对 PNG/JPEG 可用
exif_imagetype(),它更轻量且只读文件头 - 若业务允许,直接用
imagecreatefromjpeg()等函数尝试加载,失败即拦截
这些函数都会实际解析图像结构,恶意追加的内容会导致解析中断或警告,比纯 MIME 更贴近“文件是否真能被当作该类型使用”。
文件上传的 MIME 校验不是选填项,而是底线。哪怕你已做了扩展名白名单、重命名、非 Web 目录存储,只要没基于文件内容做真实 MIME 检查,攻击者就仍有缝隙可钻——尤其当服务器配置了AddHandler php-script .jpg 这类危险规则时,一个看似无害的图片就能变成 WebShell 入口。



















