必须叠加服务端内容级校验形成双保险:先白名单扩展名快速过滤,再用 finfo_open 校验真实 MIME 类型,并辅以魔数(如 JPEG 的 \xFF\xD8\xFF)精准识别文件头。

PHP 8.1 的上传接口光靠扩展名或 Content-Type 校验很容易被绕过,必须叠加至少一层服务端内容级校验,形成“双保险”。核心思路是:第一层做快速过滤(如白名单扩展名),第二层用文件真实内容说话(不看名字、不看头字段,只看二进制头和结构)。下面分三块讲清楚怎么落地。
用 finfo_open 做真实 MIME 类型校验
这是最基础也最关键的二次校验。客户端传来的 $_FILES['file']['type'] 和文件后缀都可伪造,但文件开头几个字节很难骗过系统。
- 调用
finfo_open(FILEINFO_MIME_TYPE)读取文件临时路径的二进制头 - 只允许白名单中的 MIME 类型,比如
image/jpeg、application/pdf,明确拒绝text/html、application/x-php - 注意:不要只比对
image/*这类通配符,攻击者可用 SVG(MIME 是image/svg+xml)嵌入 JS 执行 XSS 或触发解析器漏洞
检查文件头魔数(Magic Bytes)
MIME 类型可能被某些工具误报,加一层原始字节比对更稳。例如:
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
- JPEG 文件开头必须是
\xFF\xD8\xFF - PNG 必须以
\x89PNG\r\n\x1A\n开头 - PDF 必须包含
%PDF-(注意大小写和位置) - 上传 PHP 文件时,若开头是
或<script>,直接拦截</script>
强制重命名 + 目录权限隔离
即使前两层都通过,也不能让文件以原始名落地。这步不是校验,但能切断绝大多数利用链:
- 用
uniqid() . bin2hex(random_bytes(8))生成不可预测的文件名,彻底丢弃用户传来的扩展名 - 上传目录禁止执行权限(Linux 下
chmod -x uploads/),Web 服务器配置中禁用该目录的脚本解析(如 Nginx 中location ~ ^/uploads/.*\.(php|phtml|phar)$ { deny all; }) - 存储路径别用
./uploads/这种相对路径,统一走绝对路径并避开 Web 根目录,比如/var/data/uploads/
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










