php 7.4 文件上传白名单需基于 finfo_open 校验真实 mime 类型,严格比对白名单并映射安全后缀,丢弃原始文件名,上传目录须隔离且禁用执行权限,多文件须逐项校验。

PHP 7.4 中实现文件上传白名单,核心不是“允许哪些后缀”,而是“只接受哪些真实内容类型 + 强制映射安全后缀”。单纯检查 pathinfo($name, PATHINFO_EXTENSION) 或信任 $_FILES['file']['type'] 是无效的——攻击者可上传 shell.php.jpg 并伪造 MIME 为 image/jpeg,绕过前端和简单后端校验。
必须用 finfo_open 校验真实 MIME 类型
浏览器提交的 type 字段完全可伪造,唯一可信的是文件二进制头。PHP 7.4 默认启用 finfo 扩展,应这样用:
- 调用
$finfo = finfo_open(FILEINFO_MIME_TYPE)获取句柄(只需一次,复用) - 对每个上传文件,执行
$mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']) - 严格比对白名单,例如:
in_array($mimeType, ['image/jpeg', 'image/png', 'application/pdf', 'text/plain'], true) - 若返回
false或不在白名单中,直接拒绝,不进入后续流程
白名单要映射扩展名,不能依赖用户输入
即使 MIME 验证通过,也不能用 pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION) 拼接新文件名——攻击者仍可能传入 avatar.jpg%00.php 或含路径字符的文件名。正确做法是:
- 丢弃原始文件名全部信息,仅保留基础标识(如用
basename()提取不含路径的部分) - 根据验证通过的 MIME 类型,查表决定后缀:
$extMap = ['image/jpeg' => '.jpg', 'image/png' => '.png', 'application/pdf' => '.pdf']; - 生成最终文件名:
$safeName = uniqid('up_') . '_' . random_int(1000, 9999) . $extMap[$mimeType];
上传目录与权限必须隔离执行能力
白名单再严,如果文件被放到 Web 可访问且可执行的目录下,依然可能被直接请求并运行。PHP 7.4 环境中需做到:
- 上传目录**不要放在 Web 根目录内**(如
/var/www/html/uploads/是危险的) - 推荐路径:
/var/www/private_uploads/(Web 服务器用户有读写权,但无执行权) - Nginx 配置中显式禁止脚本执行:
location ~ \.(php|phar|sh|pl|py)$ { deny all; } - 目录权限设为
755,文件设为644,确保 Web 进程无法执行上传内容
多文件上传要逐项校验,不能整体判断
当使用 <input type="file" name="files[]" multiple> 时,$_FILES['files']['error'] 是数组,不能写 if ($_FILES['files']['error'] === UPLOAD_ERR_OK) ——这会触发警告且逻辑失效。
- 先获取数量:
$count = count($_FILES['files']['name']); - 用
foreach (range(0, $count - 1) as $i)遍历每个索引 - 对每个
$i单独检查:if ($_FILES['files']['error'][$i] !== UPLOAD_ERR_OK) { continue; } - 每项独立走 MIME 校验 → 映射后缀 → 重命名 → 移动,失败项单独记录日志
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











