不能信任$_files['file']['type'],因其由客户端控制;应使用finfo_open(fileinfo_mime_type)校验$_files'file',再结合exif_imagetype()验证图像结构,并严格映射mime与后缀、重命名文件。

为什么 $_FILES['file']['type'] 不能信
浏览器传来的这个字段完全由客户端控制,改个请求头就能把 shell.php 声称成 image/jpeg。一旦你用它做判断,攻击者上传的恶意脚本可能直接在 Web 目录下被执行。这不是假设——真实渗透测试中,这是最常被利用的上传绕过路径之一。
finfo_open() 怎么用才安全
必须传入 $_FILES['file']['tmp_name'],不是原始文件名或 $_FILES['file']['name'];必须用 FILEINFO_MIME_TYPE 模式,返回干净类型如 image/png,避免 FILEINFO_MIME 返回带 ; charset= 的字符串增加比对复杂度;调用前确认 extension=fileinfo 已在 php.ini 中启用,否则 finfo_open() 返回 false。
- 别用已废弃的
mime_content_type() - 也别自己
fread()+substr()手动解析魔数——规则太复杂,容易漏判 - 最小可用示例:
$finfo = finfo_open(FILEINFO_MIME_TYPE); if ($finfo === false) { die('finfo 初始化失败'); } $mimeType = finfo_file($finfo, $_FILES['file']['tmp_name']); finfo_close($finfo); if (!in_array($mimeType, ['image/jpeg', 'image/png', 'image/gif', 'image/webp'])) { die('不支持的图片类型'); }
只校验 MIME 还不够:必须配合 exif_imagetype()
MIME 类型能识别格式,但不能保证文件结构合法。攻击者可构造“伪图”:开头塞 JPEG header,后面紧跟 PHP 代码,finfo 会返回 image/jpeg,但实际执行时可能触发解析器漏洞或被 Web 服务器误当作脚本运行。
-
exif_imagetype()更轻量,只读文件头、返回整型常量(如IMAGETYPE_JPEG),不加载图像数据,性能好且专为图像设计 -
getimagesize()会返回宽高和真实 mime(注意是数组里的['mime'],不是$_FILES的),但它要求 GD 扩展启用,且对损坏图像更敏感 - 两者都必须作用于
$_FILES['file']['tmp_name'],且应在move_uploaded_file()之前调用
后缀与 MIME 必须严格映射,且强制重命名
攻击者传 shell.php.jpg,你只校验后缀是 jpg 就放行,但 Apache 可能按从右往左规则解析成 PHP;更糟的是传 ../../.htaccess 直接覆盖配置。
- 建立明确映射表:
['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp'] - 根据
$mimeType查出对应扩展名,丢弃客户端所有后缀 - 用
uniqid() . '.' . $safeExt生成新文件名,清洗原始名中的点、斜杠、空字节 - 上传目录必须不在 Web 可访问路径下;若必须放在
public/下,Nginx 需加location ~ \.(php|phtml|phar)$ { deny all; }
真正危险的不是“看不懂的代码”,而是你以为校验过了,其实只是在看攻击者想让你看到的那一层。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











