对象:用 Next.js(或类似的 JS 框架)做应用并公开上线的人,尤其是「靠 AI 帮忙做出来、运维凭感觉」的人。这是从真实事故(→ 放任CVSS 10.0的经历)中提炼出来的内容。
本站的看法:重要的不是速度,而是不漏看的机制
个人开发者在安全上出问题,大多并非知识不足,而是因为无法让运维持续下去。比谁都早读到 CVE 速报并没有价值(速度交给厂商和新闻即可)。真正有效的是让与自己相关的 CVE 不被漏看的机制。所以本文重点不在多掌握知识,而在建立这种机制。
1. 以实际运行的版本判定
「是否在用危险的版本」,要用实际运行的版本来衡量,而不是清单里的下限。因为清单里的数字与实际运行的版本常常对不上。
✗ 按 package.json 的下限计数
"next": "^16.0.0" → 误判为「16.0.0 脆弱,所以我们也脆弱」,被过度统计为 「8个脆弱」。
✓ 按实际运行的版本计数
用 npm ls next 确认实际解析出的版本 → 已自动升级的是安全的,只有固定锁定的落后了。实际是2个。
# see the versions actually resolved
npm ls next react react-dom
# inside a running container
docker exec <container> npm ls next脱字符范围(^16.0.0)会在重新构建时自动升级,固定锁定的依赖则会落后。请以这里显示的版本来判定。
如何检查自己的环境
下面的步骤可以确认应用实际运行的是哪个版本,以及已知 CVE 是否适用。每一步都只检查你自己的环境。
确认本地版本
在项目文件夹里运行 npx next info。它会显示 next、react、react-dom 的实际版本,以及与最新版相差多少。npx next -v 只输出 Next.js 的版本。
查看为什么是这个版本
npm ls next(或 pnpm why next)会显示安装了哪个版本、经由哪个包引入。如果锁定文件(package-lock.json 或 pnpm-lock.yaml)没有提交,每次构建版本都可能变化,所以先把它提交。
确认生产环境运行的版本
在本地更新,不会改变仍在运行旧镜像的服务器。如果是容器,读取运行中的容器里 next 的 package.json 的 version 字段(命令见下)。使用 output: 'standalone' 时同样有效。
与已知 CVE 对照
npm audit --omit=dev 只检查会进入生产环境的依赖。加上 --audit-level=high 后,发现 High 及以上时会以非零状态退出,CI 可以据此中止。用 osv-scanner 扫描锁定文件也能达到同样目的。
阅读适用于自己环境的官方公告
Next.js 的公告有时会按 App Router 与 Pages Router、或自托管与托管服务区分影响范围。把公告中列出的修复版本与第 1 到 3 步查到的版本进行比较。
# local: actual versions and distance from latest
npx next info
# the version running inside the production container
docker exec <container> node -p "require('next/package.json').version"
# check only production dependencies; fail on High or above
npm audit --omit=dev --audit-level=high运行方式不同,影响也不同
同一个 CVE,是否影响你取决于 Next.js 的运行方式。下面按部署方式整理两个过去的案例:
| 运行方式 | 中间件授权绕过(CVE-2025-29927) | RSC RCE(CVE-2025-55182) | 由谁更新 |
|---|---|---|---|
| 托管服务(Vercel、Netlify 等) | 不受影响(路由在另一个系统中运行) | 使用 App Router 则受影响;有拦截规则,但仍必须更新 | 你自己(锁定文件决定下一个版本) |
| 自托管(next start / standalone,含 Docker) | 受影响 | 使用 App Router 则受影响 | 你自己(重新构建镜像并重启) |
| 静态导出(output: 'export') | 不受影响(没有运行中的服务器) | 没有接收请求的服务器,一般不会波及 | 你自己(构建时的依赖仍需更新) |
所以即使使用托管服务,提升 Next.js 版本也是你自己的工作。把平台的防护当作在升级到修复版之前争取时间的手段。
也要确认支持期限。按官方支持政策,截至 2026 年 10 月:
| 版本线 | 状态(2026 年 10 月) | 安全修复 |
|---|---|---|
| 16.x | Active LTS(新功能、修复、安全补丁) | 有 |
| 15.x | Maintenance LTS(首次发布起两年) | 仅限重大问题 |
| 14.x 及更早 | 不再支持 | 严重问题时偶尔提供,没有保证 |
2. 自动监控依赖
靠人工追踪 CVE 坚持不下来,而那次遗漏就会变成事故。用工具自动监控。
免费的依赖监控
osv-scanner scan -L pnpm-lock.yaml如果用 GitHub,就启用 Dependabot(仓库的 Settings → Advanced Security → Dependabot alerts)。只有与你的依赖相关的 CVE 才会以 PR 形式送来。
关键在于优先级。本站的立场是:按「是否正在被利用(KEV)」乘以「CVSS 有多高」来排序。CVSS 再高,如果你没用到,影响就小;分数中等但正在被利用,则要最优先。这种优先级判断是工作的核心。
3. 更新:升级到修复版,而不是临时规避
出现漏洞时,把框架升级到修复版,修复漏洞本身。
表面处置(不充分)
- 只在反向代理上挡掉看起来像攻击的请求
- 停掉扣费或症状就当作「已处置」
- RCE 漏洞本身仍然留着
根本修复(正确)
- 把框架升级到修复版
- 升级后,在日志中确认攻击特征已经消失
- 把止住损害与修复根本原因当作两项独立工作,两项都做
常见的失败
「停掉了扣费或症状」≠「已处置」。止住损害和修复漏洞是两项不同的工作,两项都要做。
真实案例:2025 年 12 月的 React Server Components 漏洞
下面以 2025 年 12 月公开的 React Server Components(RSC)漏洞为例,说明处理一个已公开的 CVE 实际是什么样子。以下内容全部来自 React 和 Next.js 的官方公告以及 CISA 的公开数据。
2025 年 12 月 3 日
React 公开了 CVE-2025-55182(CVSS 10.0),无需认证即可在服务器上执行代码。在 Next.js 中,15.x、16.x 和部分 canary 版本上的 App Router 应用受影响(CVE-2025-66478);Pages Router 和 Edge Runtime 不受影响。没有临时规避办法,必须升级到修复版本(15.0.5、15.1.9、15.2.6、15.3.6、15.4.8、15.5.7、16.0.7 等)。2025 年 12 月 5 日
CISA 将其加入已确认遭到实际利用的漏洞目录(KEV)。2025 年 12 月 6 日
Next.js 补充说明:截至太平洋时间 12 月 4 日下午 1 点仍在线且未修补的应用,应更换所使用的机密(API 密钥等)。2025 年 12 月 11 日
在分析补丁的过程中,同一机制里又发现并修复了两个问题(拒绝服务和源代码泄露)。之后拒绝服务的修复被证明不完整,发布了 CVE-2025-67779,已升级的用户被要求再次升级。
由此可以得出三点运维上的教训:
- 一次更新并不是终点。同一代码的后续修复可能隔几天就来
- 平台的拦截规则只是补充,升级被明确为唯一完整的修复
- 如果在修补前处于在线状态,除了更新之外还要更换机密
本站的看法:完成的标准是生产版本已修复,而不是执行过更新
像这次一样后续修复接连发布时,「上周已经升级过」就不再可靠。判断是否完成,要看生产环境运行的版本是否达到最新公告中的修复版本,而不是凭对工作的记忆。本站也是在确认容器已重新创建、并检查其中运行的版本之后,才把更新标记为完成。
常见错误及修正方法
- 以 package.json 里的数字判断:安装的版本可能不同。用
npx next info或npm ls next确认。 - 更新锁定文件后就停下:在生产镜像重新构建并重启之前,旧版本会继续运行。确认生产环境里的版本。
- 更新一次就觉得安全了:可能还会有后续公告。保持 Dependabot 等告警开启,并在几天内持续关注公告。
- 交给平台的拦截规则:它们未必能挡住攻击的所有形式。要升级。
- 继续使用不再支持的版本线:14.x 及更早版本的修复没有保证。规划迁移到 16.x。
- 在只用于开发的依赖的噪声中漏看与生产相关的警告:先运行
npm audit --omit=dev查看会进入生产的部分。 - 忽视修补之前的那段时间:机密可能在那段时间被取走。更换 API 密钥和数据库密码(→ .env 与 API 密钥基础)。
4. 用最小权限限制损害
设计成即使应用被利用,损害也只限于局部。
以非特权用户运行
USER。这样即使被攻破,影响也只限于该用户被允许做的事。DB/Redis 只绑定到内部网络
按服务隔离
这些决定了被攻破时是止于「窃取容器内的 env」,还是发展到「整台主机被接管」。
本站自己也这样做
本站正是按本文所写的方式,对自己的依赖进行 CVE 监控,不再把引发这一切的那种遗漏(放任已公开的 CVSS 10.0)交给人的注意力。我们说「按优先级(KEV+CVSS)排序」,是因为我们自己就是这样运维的。
延伸阅读
- 事故:放任已公开的RCE而被恶意刷费的经历
- 术语:CVE / CVSS / RCE
- 历史:Log4Shell — 经由依赖的RCE
- 框架:Next.js 安全(边界、环境变量、依赖 CVE)
- 排优先级:用 CVSS、EPSS、KEV 给 CVE 排序
- 监控:osv-scanner 的设置方法
- 整体情况:Next.js 和 React 的漏洞多吗?(CVE 数据实际显示了什么)
参考资料
- Next.js「Security Advisory: CVE-2025-66478」(2025 年 12 月 3 日,12 月 6 日更新):nextjs.org
- Next.js「Security Update: December 11, 2025」(CVE-2025-55183 / 55184 / 67779):nextjs.org
- NVD「CVE-2025-55182」:nvd.nist.gov / CISA「Known Exploited Vulnerabilities Catalog」:cisa.gov
- Vercel「React2Shell Security Bulletin」:vercel.com
- Vercel「Postmortem on Next.js Middleware bypass」(CVE-2025-29927):vercel.com
- Next.js「Support Policy」:nextjs.org / 「next CLI」:nextjs.org / 「Static Exports」:nextjs.org
- npm Docs「npm audit」:docs.npmjs.com / OSV-Scanner「Usage」:google.github.io / GitHub Docs「Configuring Dependabot alerts」:docs.github.com