直接用$_files'audio'保存会覆盖同名文件且引发路径遍历、后缀绕过等高危漏洞;必须丢弃原名,用uniqid()加随机字节生成唯一文件名,结合finfo校验真实mime类型映射安全扩展名,并将文件存于web根外禁执行目录。

为什么直接用 $_FILES['audio']['name'] 保存一定会覆盖
用户上传同名音频(比如都叫 recording.mp3),如果直接拼接原文件名存到固定路径,后一次上传会直接覆盖前一次——move_uploaded_file() 不做存在性检查,它只管“移动并覆盖”。更危险的是,攻击者可构造 ../config.php 这类路径名触发目录遍历,或用 .php 后缀绕过简单扩展名检查后写入 Web 可执行目录。
生成唯一文件名必须丢弃原名 + 强制扩展名
不能只靠 uniqid() 拼接用户传来的扩展名,因为 pathinfo($_FILES['audio']['name'], PATHINFO_EXTENSION) 完全不可信。真实做法是:
- 用
finfo_open(FILEINFO_MIME_TYPE)读取临时文件二进制头,确认真实 MIME 类型(如audio/mpeg、audio/wav) - 根据 MIME 白名单映射为安全扩展名:
audio/mpeg → '.mp3',audio/wav → '.wav',其他一律拒绝 - 文件名用
uniqid('audio_') . bin2hex(random_bytes(6))生成,确保无路径分隔符、无空格、无控制字符
move_uploaded_file() 前必须校验临时文件状态
常见错误是跳过 $_FILES['audio']['error'] === UPLOAD_ERR_OK 判断,或忽略 is_uploaded_file($_FILES['audio']['tmp_name'])。这两步缺一不可:
-
UPLOAD_ERR_OK表示 PHP 层面上传成功(非网络中断、超时等) -
is_uploaded_file()防止攻击者伪造tmp_name路径读取服务器本地文件(如/etc/passwd) - 二者都通过,才允许调用
move_uploaded_file()
上传目录权限与存放位置比命名更重要
即使文件名唯一,若上传目录在 Web 根下且未禁用脚本执行,攻击者仍可能上传 shell.jpg.php 并通过 Apache 的多后缀解析漏洞触发执行。正确做法是:
- 把音频存到 Web 根外(如
/var/data/audio/),通过 PHP 脚本代理输出,或用 Nginx 的alias+location ~ \.php$ { deny all; }严格隔离 - 上传目录权限设为
0755(目录)和0644(文件),禁止组/其他用户写入 - 绝不在上传目录放
.htaccess或依赖它做防护——Nginx 用户根本不用它,且 Apache 也可能忽略
真正容易被忽略的点:MIME 校验必须在重命名前完成,且必须用 finfo 而非 $_FILES['audio']['type'];强制扩展名映射表要和白名单完全一致,不能漏掉 audio/ogg 这类现代格式;random_bytes() 在 PHP 7+ 才可用,低版本得 fallback 到 openssl_random_pseudo_bytes()。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











