fileinfo扩展未启用是首要问题,需在php.ini中启用并重启服务;上传文件前须校验$_FILES['file']['error']和is_uploaded_file();finfo_file()结果应结合文件头与白名单前缀匹配,禁用$_FILES'file'。

fileinfo扩展没启用,finfo_open直接报错
这是最常卡住的第一步:PHP默认可能关闭fileinfo扩展,调用finfo_open()会抛出Fatal error: Uncaught Error: Call to undefined function finfo_open()。不是代码写错了,是模块根本没加载。
确认方式很简单:phpinfo()页面里搜fileinfo,看“fileinfo support”是否为enabled。没开就去php.ini里取消注释这行:extension=fileinfo(Windows下可能是extension=php_fileinfo.dll),改完必须重启php-fpm或Web服务器——光 reload 不够,得真重启。
常见坑:
- 改了错误的
php.ini(CLI和Web用的配置文件可能不同,用php --ini和phpinfo()分别确认) -
extension_dir路径不对,导致找不到fileinfo.so或php_fileinfo.dll - 权限问题:扩展文件被chmod 000锁死,或SELinux阻止加载
finfo_file返回空或application/x-empty
这不是fileinfo失效,而是临时文件状态异常。上传失败、文件为空、或$_FILES['file']['tmp_name']根本没生成时,finfo_file()会返回空字符串或application/x-empty,直接拿它去白名单比对必然失败。
立即学习“PHP免费学习笔记(深入)”;
必须前置校验:
- 先检查
$_FILES['file']['error'] === UPLOAD_ERR_OK,否则跳过所有检测 - 再用
is_uploaded_file($_FILES['file']['tmp_name'])确认是合法上传临时文件,防路径穿越 - 最后才调
finfo_file(),且建议加if (!$mimeType) { die('无法识别文件类型'); }
特别注意PDF:某些生成方式怪异的PDF会被识别为application/x-empty,不能一刀切拒绝,可加fallback逻辑(比如结合pathinfo扩展名+文件头字节判断)。
MIME白名单怎么设才不翻车
只用in_array($mimeType, $allowed)不够安全。真实场景中,image/jpeg和image/pjpeg都可能是JPEG,application/pdf和application/x-pdf也可能并存。硬匹配会漏判。
推荐做法:
- 白名单用前缀匹配,比如
stripos($mimeType, 'image/jpeg') === 0,而不是===全等 - 对PDF这类易混淆类型,额外检查文件头(如读取前4字节是否为
%PDF) - 绝对不用黑名单——
!in_array($mimeType, ['php', 'exe'])毫无意义,攻击者早换新MIME绕过了 - 映射扩展名时别只认
$allowedTypes[$mimeType],要双向校验:MIME → 扩展名,且扩展名 → MIME也得能反查
为什么$_FILES['file']['type']不能信
这个值来自HTTP请求头的Content-Type字段,浏览器填什么它就传什么,用户用curl或Postman随便改个image/xxx就能绕过。它连基础防护都算不上,只能当辅助参考。
真正该依赖的是finfo_file()结果,因为它读的是文件二进制头部“魔术字节”。比如一个shell.php.jpg,哪怕扩展名是jpg,finfo也会返回application/x-php,立刻暴露本质。
但要注意:finfo也不是万能。极少数加密或损坏文件可能无法识别,此时应拒绝而非降级信任$_FILES['file']['type']——宁可误杀,不可放行。
复杂点在于,你得把finfo、扩展名、文件头、上传路径隔离四件事串成流水线,少一环都可能被绕过。最容易被忽略的是:即使MIME和扩展名都对,也得确保文件没藏WebShell——比如图片末尾拼一段<?php system($_GET[1]);?>,这就得靠内容扫描或运行时沙箱了。



















