不能只依赖后缀名校验,因后缀可被随意篡改(如shell.php.jpg),且操作系统与Web服务器按文件名解析而非内容;需结合魔数检测、MIME白名单与扩展名白名单三重校验。

上传文件时不能只依赖后缀名做校验,是因为攻击者可随意修改文件扩展名,比如把木马脚本重命名为 avatar.jpg 后上传,服务端若仅检查 .jpg 后缀就放行,该文件可能被 Web 服务器当作图片解析执行,导致远程命令执行或服务器沦陷。
后缀名完全由客户端控制
浏览器中 <input type="file"> 的 accept 属性、JavaScript 的 file.name.split('.').pop() 获取的后缀,全在用户手里。禁用 JS、用 curl 发包、改写请求体,都能绕过前端所有判断。
【后缀名不是文件内容的一部分】 操作系统和 Web 服务器根本不关心文件里写了什么,只按文件名决定如何处理——shell.php.jpg 在 Linux 下就是普通文件,但 Apache 若配置了 AddHandler php-script .jpg,它就会被当作 PHP 执行。
常见绕过手段与真实案例
方法一:大小写混淆
上传 shell.PHP 绕过只匹配小写 .php 的黑名单校验。
方法二:双重扩展名
构造 exploit.php.jpg,利用旧版 Nginx 或 IIS 的解析漏洞,优先识别最后的 .php。
方法三:空字节截断(PHP 5.3 以前)
发送 shell.php%00.jpg,%00 截断后缀校验逻辑,实际保存为 shell.php。
方法四:伪装成图片但嵌入代码
在合法 PNG 文件头 \x89PNG 后追加 PHP 代码,getimagesize() 会返回 true,但文件真实类型已是可执行脚本。
必须校验文件真实内容
第一步:读取文件前 28 字节(足够覆盖绝大多数魔数长度)
第二步:比对固定字节签名,例如 JPEG 必须以 \xFF\xD8\xFF 开头,PDF 必须以 %PDF 开头
第三步:结合 MIME 类型白名单与扩展名白名单做双重判断——即使魔数识别为 image/jpeg,也必须要求后缀是 .jpg/.jpeg/.png/.gif 之一
这一步不可跳过:仅靠 python-magic.from_buffer() 返回 image/jpeg 不够,因为 SVG 文件也可能返回相同 MIME,但它允许内嵌 JavaScript,属于高危类型。


















