PHP 7.2 中可靠文件类型检测需三重校验:禁用 $_FILES['type'],用 finfo_open() 基于 magic bytes 识别真实 MIME 类型,严格匹配扩展名与内容类型白名单,并在 move_uploaded_file() 前完成全部验证并清理临时文件。

PHP 7.2 中没有内置的“文件类型检测”功能(如判断上传文件是图片还是 PDF),所谓“文件类型检测”实际指对上传文件的 内容真实性、MIME 类型可信度和扩展名一致性 进行校验。仅依赖 $_FILES['file']['type'] 或文件后缀是危险且极易绕过的。以下是真正可靠、符合 PHP 7.2 环境的落地实践。
别信 $_FILES['type'],它由客户端控制
$_FILES['file']['type'] 是浏览器提交的 MIME 类型(如 image/jpeg),完全可被伪造,PHP 7.2 不做任何验证。即使开启 strict_types=1,它也对此字段无影响。
- 永远不将其作为唯一判断依据,哪怕只用于日志或前端提示
- 若需参考,仅可结合其他校验结果作辅助说明,例如:“用户声称上传了 PDF(
application/pdf),但实际内容为 HTML”
用 finfo_open() 做真实内容识别
PHP 7.2 默认启用 finfo 扩展(基于 libmagic),这是最推荐的内容检测方式——它读取文件头部字节(magic bytes),与已知格式特征比对。
- 示例代码:
$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', 'application/pdf'], true)) { throw new InvalidArgumentException('不支持的文件类型'); } - 注意:必须传入临时文件路径(
$_FILES['file']['tmp_name']),不能传原始文件名或 URL - 建议同时检查
FILEINFO_MIME_ENCODING判断是否为二进制(如binary),排除纯文本伪装
校验扩展名与内容是否匹配
攻击者常将恶意脚本保存为 shell.jpg 绕过扩展名校验。PHP 7.2 下应强制“扩展名 → 内容”双向验证:
立即学习“PHP免费学习笔记(深入)”;
- 提取扩展名时用
pathinfo($filename, PATHINFO_EXTENSION),并转为小写统一处理 - 建立白名单映射表,例如:
['jpg' => 'image/jpeg', 'pdf' => 'application/pdf'] - 只有当
finfo检测出的 MIME 类型与扩展名对应类型一致时,才允许通过 - 禁止使用
getimagesize()替代finfo,它仅适用于图像,且可能触发远程加载或解析异常
配合临时文件移动前的最终确认
在调用 move_uploaded_file() 前,务必完成全部校验。PHP 7.2 的严格类型模式(declare(strict_types=1))虽不直接参与文件检测,但能确保你的校验函数参数不被误传非字符串路径等错误值。
- 校验失败时,立即
unlink($_FILES['file']['tmp_name'])清理临时文件,防止磁盘堆积 - 不要在
move_uploaded_file()后再做内容检测——此时文件已落盘,风险前置 - 若业务允许,可对关键文件(如头像)额外加一层 GD/ImageMagick 打开测试,确认可正常解析



















