finfo_file()是唯一可信的mime校验方式,必须用fileinfo_mime_type模式校验tmp_name真实类型,配合白名单匹配完整mime字符串、安全重命名及存储隔离。

PHP 7.4 文件类型检测不能靠浏览器传来的 $_FILES['file']['type'],它极易被伪造;必须基于文件二进制内容做真实 MIME 判断,核心是 finfo_file() 配合严格白名单和安全重命名。
用 finfo_file() 做真实 MIME 校验
这是唯一可信的校验方式——它读取临时文件头(tmp_name),不依赖任何客户端输入。
- 必须调用
finfo_open(FILEINFO_MIME_TYPE),不能用FILEINFO_MIME(后者返回带编码信息的字符串,如image/jpeg; charset=binary,比对易失败) - 先检查
$_FILES['file']['tmp_name']是否存在、是否为合法上传临时路径,避免空值或路径穿越导致读取任意文件 - 白名单必须匹配完整 MIME 字符串,例如
'image/png'、'application/pdf',不能只写'png'或'pdf' - 上线前务必确认
fileinfo扩展已启用:extension_loaded('fileinfo')
别单独依赖 exif_imagetype()
它快但极不安全,仅适合第一道快速过滤。
- 只检查文件开头几个字节(魔数),完全无法识别“图片+PHP代码”拼接文件(如
cat good.jpg bad.php > evil.jpg) - 对 SVG 文件无效——它不解析 XML 结构,必须回退到
finfo_file()或手动扫描 XML 内容 - 返回
false不代表非法,可能是合法但不支持的格式(如含扩展块的 GIF),不能直接拒绝 - 建议组合使用:
exif_imagetype()快速排除明显非图文件 →finfo_file()做最终确认
重命名必须彻底丢弃原始文件名
用户上传 shell.php.jpg,若用 pathinfo($name, PATHINFO_EXTENSION) 提取后缀,就会得到 jpg——这正是攻击者设计的陷阱。
- 新文件名应由系统生成:推荐
uniqid('up_') . bin2hex(random_bytes(8)),再拼上由 MIME 映射来的固定后缀(如image/svg+xml → '.svg') - 映射表必须严格一一对应:
'image/gif' => '.gif',不接受非标准 MIME(如'image/x-png'应直接拒绝) - 文件名中禁止出现
..、/、\0、控制字符;清洗不如彻底不用;若真要清洗,可用preg_replace('/[^a-zA-Z0-9._-]/', '', $basename)
存储隔离比类型校验更关键
即使类型校验完美,若上传目录可执行 PHP,攻击者仍可能绕过限制运行恶意代码。
- 上传目录(如
public/uploads)必须与 Web 可执行路径物理隔离,不能放在public/下且允许脚本执行 - Linux 下设权限为
0755(所有者可读写执行,组和其他人可读可执行),确保 Web 进程能进入目录创建文件(x 权限对目录是必需的) - 确认 Web 进程用户(如
www-data或nginx)拥有该目录所有权:chown -R www-data:www-data public/uploads - Windows 下需确保 IIS/Apache 用户对该目录有“修改”权限,而非仅“读取”
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











