更新记录写的是上传扩展名黑名单没枚举全,靠黑名单挡上传为什么会漏
靠扩展名黑名单挡上传会漏,根本原因是黑名单必须提前枚举所有恶意写法:冷门后缀变体、双重扩展名、大小写与编码变形,任何清单没覆盖的形态都会默认放行。同类系统 PbootCMS 在 V3.2.16 的更新记录里就写了这样一条修复——「修复上传扩展名黑名单枚举不全导致的白名单绕过风险」,这是典型的黑名单漏法。可靠的收口做法分三层:扩展名白名单在前,文件内容类型核验在中,上传目录取消脚本执行权限兜底。
黑名单漏在哪三种写法上
| 漏点类型 | 示例形态 | 为什么黑名单拦不住 |
|---|---|---|
| 后缀变体 | 脚本文件使用清单未覆盖的冷门扩展名 | 黑名单靠枚举,新写法要有人提前补进清单 |
| 双重扩展名 | 图片扩展名后再拼接脚本扩展名 | 解析规则取前段还是末段,应用与服务器理解可能不一致 |
| 大小写与编码 | 混合大小写或 URL 编码形式的扩展名 | 比对逻辑常常只归一化了其中一种形态 |
三类漏点的共性是语义方向:黑名单是「拒绝已知」,清单之外的世界是默认通过的;攻击者只需要找到清单外的一种写法。而上传入口的写法空间由解析器、文件系统、字符编码多个环节共同决定,靠人提前枚举永远慢一步。
为什么白名单不要求预知所有攻击
白名单把默认方向反过来:允许已知,拒绝未知。业务需要接受的文件类型是有限的几类图片、文档格式,清单长度由你自己的功能决定;攻击写法的空间却是开放的。用有限清单守有限需求,比对开放空间做开放排除要稳定得多。白名单的代价是误伤罕见合法格式,所以清单要跟着业务迭代,而不是一次抄一份「通用安全配置」。
上传校验该分几层
第一层看扩展名,按白名单直接拒绝清单外后缀。第二层不看文件名,读文件头做内容类型核验,确认声明与实际内容一致,并统一重命名、按日期重新归档存储路径,切断原始文件名的解析影响。第三层在应用之外:上传目录在 Web 服务器层面取消脚本执行权限,即使前面两层都被绕过,落地的文件也不会被执行。三层各管一段,任何单层单独都不成立。
部署时同步要做的一件事
取消上传目录执行权限属于服务器配置,不在 CMS 后台里完成。用宝塔面板或 Docker 镜像部署 AnQiCMS 时,这一步应该写进部署清单和配置模板,新环境开箱即带,而不是等出事再补。要分清的是:AnQiCMS 自带的防 SQL 注入与 XSS 攻击、敏感词过滤属于内容安全层,管的是数据与展示,替代不了文件上传与目录权限这条线;反过来,上传收口也不覆盖内容审核,两道职责不要互相指望。
常见问题
已经用了白名单,还需要内容核验吗? 需要。白名单管扩展名这一关,内容核验防止「声明是图片、实际另有所含」的错位,两层拦的是不同的绕过。
双重扩展名怎么根治? 不让原始文件名参与任何解析:上传后统一重命名并归档到规则路径,解析规则就不再有可乘之机。
上传目录禁执行会不会影响正常功能? 不会,只要应用不依赖在上传目录里执行脚本。标准做法是把上传目录当纯静态资源服务,功能代码永远在程序目录运行。