
开源圈出了个挺有意思的漏洞。 9 月 26 日,知名静态网站生成器 Hugo 官方披露高危漏洞 CVE-2026-100693:它有一套用来拦截危险网络请求的"黑名单"机制,结果攻击者只要把网址里的协议名换个大小写,就能整个绕过去,去访问本该被拦下的本机和内网地址。官方已在 v0.166.0 版本修复。
很多人第一反应是:静态网站生成器?那玩意儿没有服务器、没有后端,能有什么网络风险?
这恰恰是本期要拆的误区。这个漏洞的价值不在于"又出了一个 CVE",而在于它暴露了一个写代码时极容易踩的坑——用字符串比对去实现安全策略,协议本身却根本不这么认。
一、事件复盘
先交代背景。Hugo 是 Go 语言写的静态网站生成器,全球使用者非常多,国内大量技术博客、文档站、企业官网都用它构建。所谓"静态站",就是构建时把 Markdown 等内容渲染成 HTML 文件,部署上去就行,运行期没有后端逻辑。
但 Hugo 在构建期是会联网的。比如文章里引用了远程图片、主题模板里拉取外部资源,都会通过 resources.GetRemote 这类接口发真实网络请求。
问题就出在这。从 v0.162.0 开始的多个版本(至 0.165.x),Hugo 的安全配置 security.http.urls 里有一组 IP 黑名单规则,用来禁止向指定地址发请求——典型场景就是阻止模板代码去访问 127.0.0.1(本机)、内网地址或云服务器的"元数据服务"。
安全研究员 Reload3d 发现:这些黑名单规则做的是区分大小写的字符串比对,而攻击者只要把 URL 的协议名写成混合大小写(比如把小写 http 写成大小写混杂的形式),就能骗过比对逻辑,让请求直达本该被禁止的地址。
9 月 26 日,CVE 编号注册并公开披露,官方安全公告同步发布;v0.166.0 已修复,校验逻辑改为不区分大小写。漏洞评级 CVSS 3.1 为 8.4 分(高危),攻击复杂度低、无需特殊权限。截至目前没有公开的野外利用报告,官方也未确认实际攻击案例。
二、技术核心拆解
这个漏洞最值得讲清楚的,是"字符串安全"和"协议语义"之间的缝隙。
互联网工程任务组的 RFC 3986 标准里写得明白:URL 的协议名(scheme)是不区分大小写的。也就是说,http://127.0.0.1 和 HtTp://127.0.0.1 在任何遵守标准的 HTTP 客户端眼里,是同一个协议、同一个请求。
但 Hugo 的黑名单校验是拿字符串做精确匹配。黑名单里写的是小写的协议名和地址,来了一条大小写混杂的 URL,字符串比对不上——校验放行。而真正执行请求的底层网络库按标准办事,大小写照单全收。一头按"字面"理解,一头按"语义"理解,缝隙就在这里。
再说说攻击场景,这是很多人理解偏差最大的地方。这个漏洞的攻击向量是"本地"(CVSS 向量里 AV:L),意思是:攻击发生在构建过程里,而不是网站上线之后。Hugo 构建时会处理主题、模板、shortcode 这些来源未必可信的内容,如果其中藏了恶意代码,构建那一刻它就能发网络请求。
这为什么危险?因为现代开发者的构建环境往往不干净——本地开发机上有 SSH 密钥、npm 令牌、云厂商 CLI 的登录凭证;CI 流水线里更是挂着各种 API 密钥。尤其云服务器上有个特殊地址叫"元数据服务"(比如 AWS 的 169.254.169.254),从机器内部访问它可以直接拿到临时访问凭证。SSRF(服务端请求伪造)类漏洞盯的从来都是这个。
Hugo 官方配置 IP 黑名单,防的就是这个场景:万一你装了来路不明的主题或插件,它想在构建时偷凭证,黑名单能把通往本机和元数据地址的请求拦下来。防线设计思路是对的,坏就坏在实现上被一个大小写绕穿了。
从行业规范角度看,这正是安全工程里反复强调的老原则:做访问控制,白名单优于黑名单;做规则匹配,必须按语义归一化,不能信原始字符串。大小写归一化只是最基本的一种,编码变体、Unicode 混淆、URL 解析差异,都是同一类坑。
三、常见技术误区
误区一:静态站生成器没有后端,不存在 SSRF 风险。
很多人把 SSRF 理解成"服务器被外部请求打"。实际上 SSRF 的本质是"让一台有特权网络位置的机器,替攻击者发请求"。构建机、CI 节点就是有特权网络位置的机器——它们能访问内网、能访问云元数据。Hugo 这个漏洞把"静态站很安全"的直觉打了个对折:网站运行期确实静态,构建期却是个会联网、有凭证的完整运行环境。
误区二:黑名单配好了,安全策略就落地了。
黑名单的本质是"我知道所有坏地址",但坏地址的写法是列举不完的。一个大小写就够绕过,更别说编码变形、域名解析到不同 IP 这些花活。可靠的做法是反过来用白名单:只允许访问明确放行的地址,其余一律拒绝。这次 Hugo 的修复思路也是把校验做严,但对你自己的项目而言,"默认拒绝、按需放行"才是更稳的姿势。
误区三:只访问本机地址而已,泄露不了什么东西。
本机不等于没东西。开发机上跑着的浏览器会话、本地数据库、SSH agent,CI 节点上的部署密钥,还有云元数据服务这个"一问就给凭证"的特殊存在——它们都监听在本机或内网地址上。SSRF 漏洞的危害评级之所以普遍不低,就是因为它把"外网够不着的内部世界"开了一条缝。
四、实操指引
如果你在用 Hugo,按这个顺序处理:
- 确认版本:终端执行
hugo version。凡是在 v0.162.0 到 0.165.x 之间的,都受影响。 - 升级修复:升级到 v0.166.0 或更高版本,这是唯一彻底的解法。
- 排查构建环境凭证:如果受影响版本的构建跑在 CI 或云服务器上,检查当时配置的密钥是否有异常使用记录;不放心就轮换一次。CI 里的云凭证尽量改用短期、最小权限的临时凭证,云厂商开启元数据访问防护(如 AWS 的 IMDSv2 要求令牌)。
- 审一遍主题和插件来源:
security.http.urls这类防线是"防恶意内容混进来"的最后一道闸,源头管理更重要——主题只从可信渠道装,锁定版本,别随手抄来路不明的配置。 - 自查自己的代码:如果你负责的系统里也有"字符串比对式"的校验逻辑(拦域名、拦协议、拦路径),检查匹配前是否做了大小写和编码归一化。这类 bug 平时测不出来,因为测试用例通常也是规规矩矩的小写。
对普通读者,只需要记住一句:给网站静态化不等于安全豁免,构建环节才是这类工具真正的攻击面。
五、结尾
这个漏洞的技术含量说大不大——一个 ToLower 就能修掉;说小也不小——它验证了安全圈那句老话的分量:协议语义和字符串字面之间的每一个缝隙,都会变成攻击者的入口。
对写代码的人来说,Hugo 这次相当于替所有人踩了一脚刹车:下次写"拦一下危险地址"这类逻辑时,先问自己一句——我比对的到底是什么?是字符,还是语义?
觉得有用的话,转发给还在维护 Hugo 站点的朋友,也欢迎在评论区聊聊:你项目里的黑名单校验,做过大小写归一化吗?