☰
CVE-2025-29927漏洞分析:Next.js中间件授权绕过原理与修复指南
2026/10/20 9:03:04 网站建设 项目流程

最近两天,安全社区被一个编号为 CVE-2025-29927 的 Next.js 漏洞刷了屏。简单来说,这个漏洞能让攻击者构造一个带有特殊内部标记的请求,直接绕过中间件里的授权检测逻辑,导致未登录或未授权的用户拿到受保护页面的访问权。受影响范围从 Next.js 11.1.4 一直延伸到 15.2.2,跨度极大,凡是把登录态校验、权限判断写在中间件里的项目,基本都属于高危对象。

这条消息最吓人的地方,不是“又要升级版本了”,而是它击穿了很多人默认的安全假设:我中间件都写了 token 校验,怎么可能被绕过去?这次漏洞恰恰就出在中间件和内部请求机制之间的信任边界上。如果你所在团队正在用 Next.js 做 B 端系统、后台管理、或者任何带有登录鉴权的应用,这篇文章请务必看完。我会从漏洞原理、自查方案、修复加固三个维度拆透它,最后附上我实际排查过程中踩过的坑和排查实录,希望能帮你省下几个通宵。

1. 漏洞背景:为什么中间件成了授权检测的重灾区

要理解这个漏洞为什么影响这么大,得先搞清楚 Next.js 中间件在真实项目里到底扮演什么角色。中间件(Middleware)在 Next.js 里是一个在请求进入页面或接口之前执行的函数,它运行在 Edge Runtime 上,部署位置比传统 Node.js 服务更靠前,相当于在网络入口处加了一道安检闸口。很多人习惯把登录校验、角色权限、IP 白名单、地域跳转这些通用逻辑全部塞进中间件,因为这样写最简单:不需要改动每个页面,也不需要每个接口各自校验,只要在中间件里统一判断一次就行。

这类写法的好处是肉眼可见的:代码集中、性能好、逻辑清晰。但它也埋了一个很深的隐患:中间件承载了过重的安全职责,成了整个应用“唯一的安全边界”。很多项目里,服务端接口默认信任中间件已经校验过身份,API 层不再重复验证,甚至数据层也不做归属校验。这样一来,中间件一旦被绕过,后续所有依赖它判断结果的环节全部形同虚设。这不是个别团队的坏习惯,而是 Next.js 社区里非常典型的实践方案,我自己在过往项目里也见过大量类似代码,中间件里写一个 token 判断,没有就 redirect 到登录页,仅此而已。

CVE-2025-29927 的可怕之处,就在于它精准打击了这个信任链中最薄弱的一环。根据公开分析,漏洞根源出在 Next.js 内部用于处理子请求的机制上。框架内部在处理某些嵌套请求时,会通过一组特殊的内部请求头来标记请求来源,声称自己是“内部调用”。正常情况下,外部用户请求根本不会带上这些标记,所以中间件看到这些标记时就会认为请求已经通过了前置处理,直接跳过授权检测逻辑放行。问题在于,这套内部信任机制并没有严格校验请求头是否真的来自框架内部,攻击者只需要在普通请求里手动加上这个标记,就能让中间件认为自己处理的是一个内部子请求,从而绕过所有写在中间件里的检测逻辑。

这里有一个很反直觉的点:很多人以为中间件是一道“物理上”不可绕过的防线,因为它在请求路径上,总会执行。但实际它的执行依赖于框架对请求的分类,如果框架因为某些条件把请求当作内部请求处理,中间件的业务逻辑就会被主动跳过。这就好比小区门口的保安,平时看到业主卡就放行,结果有人伪造了一张“物业内部通行证”,保安没仔细验证通行证来源就直接放行,整栋楼的业主门禁自然形同虚设。

2. 绕过原理拆解:内部请求头如何穿透防线

这次漏洞的利用核心,是 Next.js 内部使用的两个标记头:x-middleware-subrequest 和 x-middleware-override。这两个头部本意是框架内部在执行嵌套中间件、子请求、或者路由重写时用来传递状态,避免同一个请求被中间件重复处理。比如在 Next.js 内部处理 rewrites、redirects 或者某些 server action 场景时,框架会在内部请求上打上标记,告诉中间件“这个请求已经处理过一轮了,别再跑业务逻辑”。

问题就出在这个“告诉中间件”的过程。框架在检查这些头部时,似乎没有充分验证它们是否来自内部可信来源。攻击者直接在外部 HTTP 请求里手动构造一个 x-middleware-subrequest 头,值设为 src/middleware(或者对应项目中间件文件的路径),中间件读到这个头部后,会认为当前请求已经处于内部子请求的处理流程中,于是自动跳过原本要执行的授权检测逻辑,直接调用 NextResponse.next() 放行。整个绕过过程不需要任何高级技巧,不需要找到额外的注入点,更不需要组合其他漏洞,只需要一个 HTTP 客户端就能完成。

从技术细节上拆一下这个过程会更清楚。Next.js 中间件的执行流程大概是:请求到达 → 匹配中间件配置 → 执行中间件导出的函数 → 返回 Response 或继续向下处理。在这个流程中,框架会检查请求头里是否包含内部执行标记,如果包含,就不再调用中间件函数体,直接把请求往下放行。这个检查发生在业务逻辑层面之前,所以你在中间件里写了多少个 if 判断、校验有多严格,都不会被执行。攻击者等于拿到了一个逻辑上的“快捷通道”,直接绕过整个中间件函数的执行。

我在复现分析时特别注意到了一个细节,这个漏洞不是新的中间件 API 设计失误,而是新旧机制并行过程中留下的信任缺口。Next.js 从早期版本开始就有类似的内部请求处理机制,需求本身是合理的:没有这套标记,框架在处理内部跳转时会反复执行中间件,造成死循环或性能损耗。但合理的设计被错误信任就成了漏洞,开发者在设计内部协议时默认“外部请求不可能伪造内部标记”,而实际上 HTTP 请求头完全由客户端控制,服务器无法自动区分头部是浏览器原生携带的还是手工构造的,这就像给内部员工发了一张统一门禁卡,却不检查持卡人是不是真的员工,卡丢了或者被复印了,整个办公区就门户大开。

这里还要聊一个容易被忽视的点:Next.js 中间件运行在 Edge Runtime,而 Edge Runtime 与传统 Node.js 运行时在请求处理模型上存在差异。中间件在边缘节点执行时,很多开发者以为它享受了“边缘安全防护”的加成,实际上 Edge Runtime 的隔离性更强,但也意味着它更加依赖请求元信息来做判断。一旦这些元信息可以伪造,边缘执行反而放大了漏洞的影响面,因为请求在到达服务器之前,就已经被“安全放行”了一次。

需要特别说明的是,我这里只做了原理层面的技术分析,目的是帮助开发者理解漏洞成因,并不提供任何实际可利用的 PoC。安全修复的第一原则永远是先理解、再修复,而不是急于演示攻击效果。理解这个原理之后,你才能知道自己项目里哪些环节依赖了中间件的信任,哪些代码需要紧急调整。

3. 影响范围判断:先确认自己的项目是否在射程内

看到漏洞公告后,先别慌着改代码,第一步应该做影响面评估。不是所有使用 Next.js 的项目都是高危,只有同时满足以下条件才会被这个漏洞直接影响:项目使用了中间件功能、中间件里做了与安全相关的授权或鉴权逻辑、项目版本落在受影响区间内。三个条件缺一个,风险等级都会大幅下降。

先看版本。这次漏洞影响从 Next.js 11.1.4 开始一直到 15.2.2 的所有版本,跨度非常大。如果你的项目还在用 Next.js 12、13、14 的老版本,或者 15.2.2 之前的 15.x 版本,都算在受影响范围内。确认版本的方法很简单,打开项目根目录的 package.json,看一下 dependencies 里的 next 字段值,或者直接在你项目目录下运行 npm ls next 查看实际安装的版本号。这里要特别提醒,有时候 package.json 写的是 "^14.2.0",但实际安装的版本会高于这个值,所以一定要以 node_modules 里真实安装的版本为准,不能只看 package.json。

接着要看中间件代码里做了什么。很多团队把中间件当成万能工具箱,什么逻辑都往里扔。如果中间件里只有一些纯展示逻辑,比如根据用户设备类型做页面跳转、根据语言偏好设置 locale,那即使被绕过,造成的危害也比较有限。但如果中间件里做了这些事中的任何一件,就要高度警惕了:检查用户是否登录、判断用户角色是否允许访问某路由、校验 JWT 或 session 有效性、基于 IP 做访问限制。这些都属于授权检测逻辑,一旦绕过,攻击者可以直接访问原本受保护的页面和接口。这里我列一个简单的自查表,你可以对照着给自己的项目打一下分:

场景是否受影响风险等级
中间件里只做页面跳转/语言切换受影响但危害低低
中间件里做登录状态检查,但接口层还有二次校验受影响,但危害可控中
中间件里做登录检查,接口层直接信任透传受影响,危害严重高
中间件控制整站路由权限,无其他防护受影响,危害严重严重
项目完全不用中间件,鉴权全写在服务端接口不受影响无

还有一个很容易漏掉的点:即使你的项目没有使用中间件,只要使用 Next.js 框架且版本在受影响区间内,你仍然需要升级。因为这属于框架底层的信任缺陷,不排除未来在其他场景下被利用的可能。安全修复的原则从来都是“修补所有已知风险面”,而不是“修补正在被利用的那个点”。

另外一定要检查 CI/CD 构建流程。如果你们的构建镜像里锁定了旧版本依赖,即使代码里已经改了中间件逻辑,发布出去的产物依然可能带漏洞。这种问题我在实际项目中遇到太多次了:开发环境升级了,测试环境升级了,生产环境的 Dockerfile 里还写着一个上古版本的 npm install,导致线上跑的还是几百天前的依赖。所以自查时,一定要把锁文件、Dockerfile、构建脚本里的版本约束全部拉出来过一遍。

到这里,如果确认项目确实受影响,别紧张,后面给出的修复方案可以在不改变业务逻辑的前提下快速解除风险。

4. 修复与加固:不只有升级一条路

绝大多数安全漏洞的标准修复方案都是“升级到修复版本”,这次也不例外。根据公开的补丁信息,Next.js 15.2.3、14.2.25、13.5.9 等版本已经修复了该问题,后续的 15.x 版本均不受影响。我的建议是:如果你的项目工程上允许,直接升级到当前最新的稳定版,而不是只升到最低修复版本,因为新版本还会持续获得其他安全补丁和性能改进。

升级动作本身不复杂,核心就一条命令:

npm install next@15.2.3 --save-exact

如果你用的是 pnpm 或 yarn,对应换成 pnpm add next@15.2.3 --save-exact 或 yarn add next@15.2.3 --exact。之所以建议 --save-exact 而不是继续使用 ^ 前缀,是避免后续依赖更新时,npm 自动拉取比你预期更高的版本,导致构建结果不可控。这一点对于安全修复尤其重要:你需要确定线上环境跑的就是打了补丁的版本,而不是“某个可能打了补丁的版本”。

但升级只是基础操作,我强烈建议在升级之外再叠加两层加固,因为这次漏洞暴露出的问题远不止一个请求头那么简单。第一层加固是网关层过滤。如果你的服务前面有 Nginx、网关或者 CDN,可以加一条规则,把 x-middleware-subrequest、x-middleware-override 这两个请求头直接拦截掉。这是一个成本极低、效果立竿见影的缓解措施,而且不依赖 Next.js 版本。在 Nginx 里可以这样写:

if ($http_x_middleware_subrequest) { return 403; } if ($http_x_middleware_override) { return 403; }

CDN 产品一般也有自定义 Rules Engine 或边缘函数,可以写一条规则丢弃这两个头。这样即使攻击者手工构造请求,头部到达应用服务器之前就被干掉了,中间件不会再看到这些标记。需要提醒的是,这个方案只适合作为临时缓解措施,不能替代版本升级。因为中间件内部协议未来可能还会新增其他内部头部,只过滤已知的两个头存在挂一漏万的风险。

第二层加固是代码层面的纵深防御。前面已经提到,很多项目的中间件承载了过多信任,API 层完全不设防。这次漏洞给所有人上了一课:即使中间件看起来完美执行了授权校验,也必须假设它可能被绕过。正确的姿态是把授权判断下沉到数据访问层或服务端接口层,至少对敏感操作做二次校验。比如在服务端页面或 API Route 里,重新读取用户身份信息,再判断是否允许访问,而不是只依赖中间件设置的请求标记。下面这个示例展示了在 API Route 里做二次校验的基本思路:

export async function GET(request) { const session = await getSession(request); if (!session) { return new Response(JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }); } const data = await loadSensitiveData(session.userId); return Response.json(data); }

这段代码的核心意图是:即使中间件被绕过,攻击者直接请求这个 API,服务端依然能够识别未授权身份并拒绝返回数据。这才是真正的纵深防御——每一层都独立承担安全职责,而不是把宝全部押在某一层上。

在升级和加固的同时,还有一些细节值得关注。Next.js 升级到新版本后,中间件的 API 行为可能发生细微变化,特别是从老版本直接跳到大版本升级时,可能会出现 matcher 配置兼容问题或运行时差异。所以升级完成后,一定要完整回归一遍涉及中间件路由的用例,包括登录跳转、权限拦截、页面直达等关键路径,确保业务逻辑没有因为框架升级而意外改变。

5. 常见问题与排查实录

这次漏洞公告发布后,不少同行在社区和群里问了各种问题。我结合自己这几天帮几个项目做排查的经历,整理了几个高频问题,希望对你排查时有所帮助。

第一个高频问题:我升级到了 15.2.3,扫描器还是报告漏洞,是怎么回事?这种情况大概率是依赖锁定问题。你升级的是项目直接依赖,但如果某个间接依赖里也引用了 Next.js,或者 node_modules 里存在多个 next 副本,扫描器会逐个检查。排查方法是在项目目录下运行 npm ls next,看看依赖树中是否有未升级的其他副本。如果存在,需要检查是不是某个本地包把 next 锁死了旧版本,必要时通过 overrides 或 resolutions 强制指定修复版本。

第二个问题:中间件里的 IP 白名单还能用吗?这个问题问得很有代表性。答案是:升级后可以用,但不要单独依赖 IP 白名单做安全边界。IP 白名单本身就是一种较弱的认证手段,攻击者可以通过代理池绕过。更合理的做法是 IP 白名单只作为额外的访问控制维度,与登录态校验配合使用。如果业务允许,优先使用更细粒度的网络访问控制,比如在网关层就限制来源 IP,而不是等到应用层再做判断。

第三个问题:没有用中间件,但用了 Next.js 的 rewrites 和 redirects,会不会受影响?rewrites 和 redirects 功能在框架内部也涉及请求处理流程,但漏洞的关键触发点在中间件执行机制。如果项目没有使用中间件,也没有把授权检测依赖在中间件流程上,那么单独使用 rewrites 和 redirects 并不会直接导致授权绕过。但稳妥起见,仍然建议升级到修复版本,因为框架底层的不安全信任机制已经有了“前科”,不排除后续研究人员在相关路径上找到变体利用方式。

第四个问题:升级之后,原本正常的页面跳转突然不执行了,是怎么回事?我在一个项目里正好遇到过类似现象。排查后发现是中间件 matcher 配置与新版版本解析规则存在差异导致的。旧版本对 matcher 的路径匹配比较宽松,新版本则要求更严格的前缀写法。比如之前写 matcher: ['/dashboard'],新版本可能只匹配精确路径 /dashboard,而 /dashboard/123 不再匹配,需要用 /dashboard/:path* 才能完整覆盖。这个问题不是版本 bug,而是配置规则的行为变更,升级后需要手动检查一遍。

除了这些问题,我还想分享一个排查技巧:如果怀疑线上已经被尝试利用,可以检查访问日志里是否有可疑的内部请求头。大多数应用默认不会记录请求头,所以在 Nginx 或网关层可以临时开启请求头日志,过滤与中间件相关的路由,看看是否有来源 IP 频繁发送带 x-middleware-subrequest 的请求。这不是一个常规动作,但在漏洞爆发期做一次应急排查非常值得。

最后分享一点我自己的体会。这次漏洞让我最深的感触是,安全的本质是信任边界管理。Next.js 设计内部请求标记时,信任了“外部请求不会携带内部标记”这个假设,而攻击者的工作恰恰就是打破假设。对于应用开发者来说,唯一可靠的策略就是不再假设任何单点是绝对安全的。中间件可以做效率工具、可以做统一入口,但绝不能成为唯一的安全防线。把授权判断下沉一层,虽然多写几行代码,但换来的安全冗余是完全值得的。希望这次的漏洞分析,能帮你少踩一个坑。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询