splfileinfo::getextension()仅截取文件名最后一个点后的字符串,不验证内容,不可用于安全校验;真实类型须用finfo_open()+finfo_file()读取magic bytes获取mime,再映射并比对扩展名白名单。

用 SplFileInfo 读取文件扩展名,但别信它返回的 getExtension()
SplFileInfo::getExtension() 只是简单截取文件名中最后一个 . 后的内容,完全不检查文件内容。比如一个伪造的 photo.jpg.php(实际是 PHP 脚本),它会返回 php;而一个被重命名的 image.png(实际是 ZIP 文件),它也会老老实实返回 png。这在上传校验、安全过滤等场景下等于没校验。
真实后缀必须靠 MIME 类型 + 文件头(magic bytes)判断
PHP 原生不提供“识别真实类型”的函数,得组合使用:finfo_open()(基于 libmagic)读取二进制头部,再映射到扩展名。注意:SplFileInfo 本身不参与这个过程,它只负责路径和基础元信息。
-
finfo_open(FILEINFO_MIME_TYPE)返回类似image/jpeg的 MIME,比mime_content_type()更可靠(后者已废弃) - 把 MIME 映射为扩展名需自己维护映射表,例如
['image/jpeg' => 'jpg', 'application/pdf' => 'pdf'],不能依赖pathinfo($file, PATHINFO_EXTENSION) - 务必用
finfo_file()传入文件路径(或资源),而不是字符串内容——否则可能因编码/换行问题误判
完整验证流程:先查 MIME,再比对扩展名是否匹配白名单
典型上传校验逻辑不是“取后缀 → 判断是否在白名单”,而是“取真实 MIME → 映射预期扩展名 → 检查文件名扩展名是否一致且属于允许类型”。这样能防重命名攻击。
$info = new SplFileInfo($_FILES['upload']['tmp_name']);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $info->getRealPath());
finfo_close($finfo);
$allowedTypes = [
'image/jpeg' => ['jpg', 'jpeg'],
'image/png' => ['png'],
'application/pdf' => ['pdf'],
];
$ext = $info->getExtension();
if (!isset($allowedTypes[$mimeType]) || !in_array($ext, $allowedTypes[$mimeType], true)) {
throw new RuntimeException("文件类型不合法:MIME {$mimeType},扩展名 {$ext}");
}
容易踩的坑:临时文件路径、open_basedir、finfo 缓存
SplFileInfo 构造时若传入的是 $_FILES['x']['tmp_name'],要立刻调用 getRealPath() 获取绝对路径——因为某些 SAPI(如 CLI)下 tmp_name 可能是相对路径,finfo_file() 会失败。
- 如果启用了
open_basedir,确保临时目录在允许范围内,否则finfo_file()返回false -
finfo_open()可以复用,但不要在长生命周期脚本(如常驻进程)里全局单例——libmagic 数据库加载有开销,且部分版本存在线程安全问题 - 别用
$_FILES['x']['name']的扩展名做任何安全判断,用户可控字段,毫无意义
SplFileInfo 在这件事里只是个“带路的”,核心判断必须交给 finfo 和你自己的映射逻辑。漏掉 MIME 校验或硬编码扩展名映射,都可能让恶意文件绕过检查。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











