$_files'file'不可信,因其由浏览器伪造而非php检测;应使用finfo_open()基于文件内容识别mime类型,并辅以getimagesize()、文件头校验、重命名、目录权限控制等多重防护。

为什么 $_FILES['file']['type'] 不可信?
浏览器自己填的 $_FILES['file']['type'],不是 PHP 检查出来的——它只是 HTTP 请求里带过来的 Content-Type 字段,用户改个浏览器开发者工具就能伪造。靠它判断文件类型,等于用门牌号当锁芯。
真正该做的是:忽略这个字段,改用服务端真实检测。
- 用
finfo_open()做 MIME 类型探测(推荐) - 对图片类文件,可加
getimagesize()二次验证 - 对 ZIP/PDF 等格式,检查文件头(magic bytes)更稳妥
怎么用 finfo_open() 安全判断上传文件类型?
finfo_open() 是 PHP 内置函数,基于 libmagic 库,读取文件实际内容来识别类型,比扩展名和 $_FILES['file']['type'] 可靠得多。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 必须传入
FILEINFO_MIME_TYPE,不要只用FILEINFO_MIME(后者会带编码,难匹配) - 检测前先确认
$_FILES['file']['error'] === UPLOAD_ERR_OK,否则tmp_name可能为空 - 用
finfo_file()传入临时文件路径,别尝试读整个文件内容再检测
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, ['image/jpeg', 'image/png', 'image/gif'])) {
die('不支持的文件类型');
}
只校验 MIME 类型还不够?这些边界情况要手动堵住
哪怕 finfo_file() 返回 image/jpeg,也不能直接当成安全图片——攻击者可能把恶意代码塞进 JPEG 的 EXIF 段,或构造畸形文件绕过检测。
关键补漏点:
- 对图片上传,额外调用
getimagesize($_FILES['file']['tmp_name']);返回false就说明不是合法图像 - 限制上传文件大小(
upload_max_filesize和$_FILES['file']['size']双重检查) - 绝不信任原始
$_FILES['file']['name'],重命名时强制指定后缀(如uniqid() . '.jpg'),并去掉所有路径字符(basename()过滤) - 上传目录禁止执行权限,且不要放在 Web 根目录下(例如放到
/var/uploads/而非./uploads/)
常见报错:Undefined index: file 或 UPLOAD_ERR_NO_FILE
这两个错误根本不是类型问题,而是表单或传输环节出错,但新手常误以为是 MIME 判断逻辑错了。
排查顺序:
- 确认 HTML 表单有
enctype="multipart/form-data" - 确认
<input type="file">的name属性和 PHP 中$_FILES键名完全一致(区分大小写) - 检查是否设置了
max_file_uploads或post_max_size过小,导致文件被截断 -
UPLOAD_ERR_NO_FILE多半是用户没选文件就提交,不是后端逻辑问题











