必须结合文件头(magic bytes)、MIME类型和后缀白名单三重校验,禁用$_FILES'file',清理文件名并强制小写后缀比对,拒绝路径遍历与非法字符。

只靠 $_FILES['file']['name'] 取后缀是不安全的
用户传来的文件名完全可控,比如把 evil.php 改成 song.mp3,后端若只检查字符串后缀,就会被绕过。真实场景中,攻击者常配合 MIME 伪造、空字节截断或双扩展(如 shell.php.jpg)来突破校验。
必须结合以下至少两种方式交叉验证:
- 从
$_FILES['file']['tmp_name']读取文件头(magic bytes),用finfo_open()判断真实类型 - 白名单限制允许的后缀(不是黑名单!)
- 拒绝含多个点、非 ASCII 字符、路径遍历符号(如
../)的文件名
用 finfo_open() 校验音频 MIME 类型最可靠
finfo_open() 基于文件内容而非扩展名做识别,对常见音频格式支持良好。注意它依赖系统 libmagic 数据库,需确保 PHP 编译时启用了 fileinfo 扩展(可通过 extension_loaded('fileinfo') 检查)。
实操示例:
立即学习“PHP免费学习笔记(深入)”;
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $_FILES['audio']['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, ['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/flac', 'audio/mp4'], true)) {
die('不支持的音频格式');
}
注意:audio/mp4 对应 .m4a/.mp4 音频;audio/x-wav 和 audio/wav 都可能出现,建议统一 normalize 或用 stripos 模糊匹配。
后缀白名单 + 文件名清理不能省
即使 MIME 校验通过,仍要提取并验证后缀——因为上传路径、CDN 分发、前端播放器都依赖扩展名。错误做法:pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION) 直接取,会受恶意文件名干扰。
安全提取后缀的步骤:
- 先用
basename()去掉可能的路径部分(如../../shell.php) - 再用正则过滤非 ASCII 和控制字符:
preg_replace('/[^\w.-]/u', '', $filename) - 最后用
pathinfo()取扩展名,并转小写统一比对 - 白名单严格限定为:
['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac']
特别注意:Windows 下大小写不敏感,但 Linux 服务器和某些 CDN 是区分的,强制小写可避免歧义。
为什么不用 $_FILES['file']['type']?
$_FILES['file']['type'] 是浏览器提交的 Content-Type,完全由客户端控制,毫无可信度。例如 curl 可轻易伪造:-F "file=@hack.php;type=audio/mpeg",后端若据此放行,就等于开了后门。
这个字段唯一合理用途是辅助日志记录,**绝不可用于任何逻辑判断或权限控制**。所有关键校验必须基于服务端读取的文件内容或可信元数据。
真正难处理的是某些边缘格式(如 AIFF、AMR)或带 DRM 的音频,它们的 magic bytes 可能不标准,或者 finfo 数据库版本老旧。这种情况下,宁可拒收,也不要降级到仅靠后缀判断。



















