必须校验上传错误码、用finfo检查真实MIME类型、生成内容决定的随机文件名,并将上传目录置于Web根目录外且禁用脚本执行。

直接用 move_uploaded_file() 移动音频文件是危险的,不校验、不重命名、不查内容,等于给攻击者留后门。
必须检查的三个上传错误码
音频文件体积常较大,UPLOAD_ERR_INI_SIZE 和 UPLOAD_ERR_FORM_SIZE 出现频率远高于其他错误。只写 if ($file['error'] === 0) 会掩盖真实问题,用户看到“上传失败”却不知原因。
-
UPLOAD_ERR_INI_SIZE:PHP 配置中upload_max_filesize不够(比如设为 2M,但用户传了 5MB 的 MP3) -
UPLOAD_ERR_FORM_SIZE:表单里没设<input type="hidden" name="MAX_FILE_SIZE" value="...">,或post_max_size小于upload_max_filesize,此时$_FILES可能为空数组,直接访问会触发 Notice -
UPLOAD_ERR_NO_FILE:用户点了提交但没选文件,isset($_FILES['audio'])为 true,但['error'] === 4,跳过判断就直接往下走会出错
正确做法是显式 switch:
switch ($file['error']) {
case UPLOAD_ERR_OK:
// 继续处理
break;
case UPLOAD_ERR_INI_SIZE:
die('文件超出服务器限制,请联系管理员调整 upload_max_filesize');
case UPLOAD_ERR_FORM_SIZE:
die('表单数据超限,请减小文件或确认服务端 post_max_size 设置');
case UPLOAD_ERR_NO_FILE:
die('未选择音频文件');
default:
die('上传异常:' . $file['error']);
}
别信 $_FILES['type'],用 finfo 检查真实 MIME
浏览器上报的 $_FILES['audio']['type'] 可被任意篡改,例如把 shell.php 改名成 voice.mp3 并伪造 type="audio/mpeg",就能绕过前端和简单后端检查。
立即学习“PHP免费学习笔记(深入)”;
必须用 finfo_open() 读取文件头(magic bytes)来确认类型:
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
$allowedTypes = ['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/flac'];
if (!in_array($mimeType, $allowedTypes)) {
die('不支持的音频格式');
}
注意:audio/mpeg 覆盖 MP3,audio/wav 是 PCM/WAV,audio/ogg 对应 Ogg Vorbis 或 Opus——不要只写 audio/mp3,那不是标准 MIME。
生成安全文件名:丢掉原始扩展名,靠内容推断
保留 pathinfo($file['name'], PATHINFO_EXTENSION) 是高危操作。攻击者可传 exploit.php.wav,你取到 wav 就存,但 Web 服务器若配置不当,仍可能执行 .php 部分。
正确流程是:
- 完全丢弃
$file['name']中的扩展名 - 根据
finfo返回的$mimeType映射出可信扩展名(如audio/mpeg → .mp3) - 用
random_bytes(16)生成不可预测前缀,避免哈希碰撞和时间预测
$extMap = [
'audio/mpeg' => '.mp3',
'audio/wav' => '.wav',
'audio/ogg' => '.ogg',
'audio/flac' => '.flac',
];
$ext = $extMap[$mimeType] ?? '.bin';
$safeName = bin2hex(random_bytes(16)) . $ext;
$destination = '/var/www/uploads/audio/' . $safeName;
这样既防覆盖,也杜绝路径遍历(../ 在 basename() 后已失效),更关键的是——扩展名由内容决定,而非用户输入。
上传目录权限与执行隔离必须做
即使文件名和类型都安全,如果上传目录允许 PHP 解析执行,攻击者仍可能通过其他手段(如解析 .htaccess、利用旧版 Nginx 文件类型误判)触发代码执行。
生产环境必须:
- 将音频目录放在 Web 根目录之外(如
/var/www/uploads/audio/),并通过脚本代理下载 - 若必须放 Web 可访问路径,需在该目录下禁用脚本执行(Apache 加
php_flag engine off,Nginx 加location ~ \.php$ { deny all; }) - 确保上传目录无 world-writable 权限(
chmod 755足够,绝不用777)
最后一点容易被忽略:音频文件也可能携带恶意元数据(如 ID3v2 标签注入 JS),虽然不直接导致 RCE,但在某些富客户端播放器中可能触发 XSS。如需高保障,应对元数据做清理(例如用 getID3 库剥离非必要字段)。



















