最可靠的方式是使用 finfo_file() 进行内容级 MIME 类型识别,需用 FILEINFO_MIME_TYPE 初始化、校验 $_FILES'file' 并严格白名单匹配;exif_imagetype() 仅作快速初筛,getimagesize() 和 mime_content_type() 不推荐用于安全校验。

PHP文件类型校验不能只看扩展名或浏览器传来的$_FILES['file']['type'],这些全可被伪造。真正可靠的方式是读取文件二进制头——finfo_file() 是目前唯一被官方推荐、生产环境广泛验证的方案。
finfo_file():最可靠的内容级识别
它调用系统 magic 库解析临时文件前若干字节,返回真实 MIME 类型(如 image/png),不受文件名、HTTP 头、编码影响。使用时必须注意:
- 初始化必须用
finfo_open(FILEINFO_MIME_TYPE),不能用FILEINFO_MIME(后者带charset=后缀,比对易失败) - 校验对象必须是
$_FILES['file']['tmp_name'],且需先确认is_uploaded_file()返回 true,防止路径穿越 - 白名单匹配要完整精确:
'image/jpeg'✅,'jpeg'或strpos($mime, 'image/') !== false❌ - 上线前必须检查
extension_loaded('fileinfo'),老旧环境可能未启用该扩展
exif_imagetype():仅作快速初筛
轻量、快,但只检查 JPEG/GIF/PNG 等常见图像魔数(如 JPEG 的 FF D8 FF),存在明显盲区:
- 无法识别拼接型恶意文件(如
cat good.jpg bad.php > evil.jpg) - 对 SVG 完全无效(不解析 XML 结构)
- 遇到 GIF 扩展块可能误判;返回
false不代表非法,可能是合法但不支持的变体 - 建议仅作为第一道过滤:快速排除明显非图文件,之后仍须交由
finfo_file()终审
getimagesize() 和 mime_content_type():不推荐用于安全校验
getimagesize() 只支持图像格式,对视频、PDF、文本等直接返回 false 或警告,无法分类;mime_content_type() 已被废弃,行为不稳定,且不支持自定义 magic 路径,在不同系统上结果可能不一致。
立即学习“PHP免费学习笔记(深入)”;
- 二者都不读取完整文件头,也不校验魔数,可靠性远低于
finfo_file() -
getimagesize()可用于后续图像处理(如缩略图生成),但绝不能用于上传类型判断 - 不要用
pathinfo($name, PATHINFO_EXTENSION)提取后缀——攻击者专设shell.php.jpg这类陷阱
魔数手动校验:补充手段,非替代方案
对高敏感场景(如头像、富文本上传),可在 finfo_file() 基础上追加魔数比对(如读取前 4–16 字节转 hex,比对 89504E47 判 PNG)。但要注意:
- 仅覆盖主流格式,无法穷举所有类型;PDF、Office 文档魔数较复杂,易漏判
- 需自己维护魔数表,维护成本高;而
finfo底层已内置成熟 magic 数据库 - 适合与
finfo联用(例如:finfo 判为image/png后,再验证前 4 字节是否为89504E47),不可单独依赖



















