不能信$_files'file',因其由客户端控制易被篡改;必须用finfo_open()读取临时文件内容校验mime类型,并配合exif_imagetype()验证图像结构,再结合扩展名检查、目录权限限制和强制重命名四层防护。

为什么不能信 $_FILES['file']['type']
浏览器传来的这个字段完全由客户端控制,改个请求头就能把 shell.php 声称成 image/jpeg。一旦你用它做判断,攻击者上传的恶意脚本可能直接在 Web 目录下被执行。这不是假设——真实渗透测试中,这是最常被利用的上传绕过路径之一。
必须用 finfo_open() 读取临时文件内容
PHP 自带的 finfo_open() 是目前最可靠的服务端 MIME 探测方式,它基于文件前几百字节的 magic number 匹配,不依赖扩展名或头部注释。
- 务必传入
$_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() 或 getimagesize()
MIME 类型能识别格式,但不能保证文件结构合法。攻击者可构造“伪图”:开头塞 JPEG header,后面紧跟 PHP 代码,finfo 会返回 image/jpeg,但实际执行时可能触发解析器漏洞或被 Web 服务器误当作脚本运行。
-
exif_imagetype()更轻量,只读文件头、返回整型常量(如IMAGETYPE_JPEG),不加载图像数据,性能好且专为图像设计 -
getimagesize()会返回宽高和真实mime(注意是数组里的['mime'],不是$_FILES的),但它要求 GD 扩展启用,且对损坏图像更敏感 - 两者都必须作用于
$_FILES['file']['tmp_name'],且应在move_uploaded_file()之前调用
推荐组合校验:
$tmp = $_FILES['file']['tmp_name'];
if (exif_imagetype($tmp) !== IMAGETYPE_JPEG && exif_imagetype($tmp) !== IMAGETYPE_PNG) {
die('非有效 JPEG/PNG 图像');
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $tmp);
finfo_close($finfo);
if ($mime !== 'image/jpeg' && $mime !== 'image/png') {
die('MIME 类型不匹配');
}
别忘了扩展名、目录权限和重命名这三道防线
MIME 和图像结构校验解决的是“内容可信”,但还有三个现实风险点必须堵住:
- 扩展名检查只是初步过滤,用
pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION)获取,转小写后白名单比对(如in_array(strtolower($ext), ['jpg', 'jpeg', 'png']));但绝不能单独依赖它 - 上传目录必须禁用脚本执行权限:Apache 下用
.htaccess加php_flag engine off,Nginx 则在 location 块里加deny all;或限制fastcgi只处理特定后缀 - 落地前强制重命名文件,丢弃原始文件名——防止 Unicode、空格、路径遍历(如
../shell.php)或已知恶意文件名被复用
真正安全的上传,从来不是靠某一个函数兜底,而是让攻击者必须同时绕过四层独立校验:扩展名 → MIME → 图像结构 → 目录执行控制。少一层,风险就指数级上升。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











