php 7.4 中校验上传文件类型必须用 finfo_file() 检测真实 mime 类型,配合安全重命名、严格存储路径控制及多层加固措施,杜绝依赖 $_files'file' 或扩展名。

PHP 7.4 中限制上传文件类型并确保安全,不能依赖浏览器传来的 $_FILES['file']['type'] 或用户提交的扩展名——这两者均可被轻易伪造。真正有效的校验必须基于文件内容本身,并配合路径、命名、存储等多层防护。
✅ 真实 MIME 类型检测:用 finfo_file() 校验二进制头
这是 PHP 7.4 下唯一可信的类型识别方式:
- 调用
finfo_open(FILEINFO_MIME_TYPE)(注意不是FILEINFO_MIME,否则返回带编码的字符串如image/jpeg; charset=binary,比对会失败); - 传入
$_FILES['file']['tmp_name'],且必须先确认该路径存在、可读、是合法上传临时文件; - 返回值如
'image/png'、'application/pdf',需与白名单完整字符串精确匹配(不能只写'png'或用strpos()模糊判断);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
if (!$finfo) {
die('finfo 扩展未启用');
}
$mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
$allowedTypes = [
'image/jpeg',
'image/png',
'image/gif',
'application/pdf',
'text/plain'
];
if (!in_array($mimeType, $allowedTypes)) {
die('不支持的文件类型:' . htmlspecialchars($mimeType));
}
⚠️ 上线前务必检查:
extension_loaded('fileinfo'),老旧环境可能默认关闭该扩展。
✅ 安全重命名:彻底丢弃原始文件名
攻击者常上传 shell.php.jpg,靠 pathinfo($name, PATHINFO_EXTENSION) 提取 .jpg 来绕过校验。正确做法是:
- 完全忽略
$_FILES['file']['name']的任何部分; - 用
uniqid('up_') . bin2hex(random_bytes(8))生成唯一前缀; - 后缀由 MIME 映射表严格决定(如
'image/svg+xml' => '.svg'),拒绝非标准类型(如'image/x-png'); - 文件名中禁止
..、/、\0、控制字符;若需清洗,可用:$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '', $basename);
✅ 存储路径与权限控制:防止执行与遍历
- 目标目录(如
public/uploads/)不能放在 Web 可直接访问路径下,或至少禁用脚本执行(Nginx/Apache 配置deny all;或AddHandler default-handler .php); - Linux 下目录权限设为
0755(必须含x位,否则 Web 进程无法进入创建文件); - 创建前校验:
$targetDir = '/var/www/myapp/uploads/'; if (!is_dir($targetDir) || !is_writable($targetDir)) { die('上传目录不可写或不存在'); } -
move_uploaded_file()前务必确认目标路径是绝对路径、不含用户输入拼接,避免路径遍历。
✅ 补充加固(按需选用)
-
大小限制:
$_FILES['file']['size']+php.ini中upload_max_filesize和post_max_size双重控制; -
初筛加速(可选):对图片类可先用
exif_imagetype()快速排除明显非图文件,但绝不能替代finfo_file(); -
深度验证(高风险场景):PDF 可用
tcpdf解析结构;SVG 应扫描<script></script>标签;图像类可加getimagesize()辅助确认; - 日志记录:记录上传时间、IP、原始名、MIME、保存路径,便于审计。
不复杂但容易忽略。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











