一些概念了解
Next.js是一个由Vercel开发的基于React的Web开发框架,它是一个“全栈框架”,开发者可以使用javascript或者typescript开发一个完整包含前后端的网站,而且前后端的代码都放在一个项目里,编写十分方便。
Next.js的后端服务,可以使用两种模式来运行,分别是node.js和edge。node.js是默认的运行模式,可以理解为代码运行在传统的node.js服务器上,他没有任何限制,可以用来操作文件、数据库等等;edge是将代码运行在边缘计算服务上,比如Vercel Edge Functions和Cloudflare Pages等。
我们可以将edge理解为一种“受限”的后端处理逻辑,它的代码运行在全球各地的CDN服务器中,离用户更近,非常适合用于处理一些“轻工作”,比如数据校验、鉴权、国际化等。但也是因为其运行在CDN节点中,它无法使用node.js的原生模块,比如文件操作等。
next.js中提供了大部分传统Web开发框架都会有的功能——middleware(中间件)。和传统后端框架相同的是,middleware在next.js中也常用来检查用户身份、国际化、设置HTTP头等;和传统后端框架不同的是,next.js的middleware一定运行在edge模式下,不一定会和后端逻辑运行在一起。
CVE-2025-29927这个漏洞就是位于middleware中。一个middleware用于检查用户身份是否合法,由于检查的过程需要查询数据库,所以它会发送一个新的请求访问用户校验API。 此时会出现一个问题,由于next.js是“全栈”框架,这个检查用户身份的API也会经过middleware,这样数据流就会进入递归死循环。next.js为了解决这个问题,就为middleware中发出的请求增加了一个x-middleware-subrequest头,每次发送请求时值添加一个“middleware”字符串;并且在收到带有x-middleware-subrequest的头且发现递归超过5次后,就不再执行middleware,这样middleware中发出的请求不会再有递归问题。 那么漏洞复现方法就呼之欲出了,攻击者只要在发送给next.js的请求中增加一个头x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware,就可以绕过next.js的中间件。如果开发者将校验用户身份的逻辑放在中间件里,就将导致认证绕过漏洞。
1 用户请求 → 中间件A(身份验证) → 发起API请求 → 中间件A再次执行 → 再次发起API请求 → ...
漏洞描述
Next.js 使用内部头字段“x-middleware-subrequest”来防止递归请求导致的无限循环。攻击者可以通过在请求中伪造该头字段,跳过中间件的执行,从而绕过关键的安全检查,例如授权 Cookie 验证。
受影响的版本
Next.js 15.x < 15.2.3
Next.js 14.x < 14.2.25
Next.js 13.x < 13.5.9
漏洞复现
这里的漏洞环境我们直接用p神的vulhub。
https://github.com/vulhub/vulhub/blob/master/next.js/CVE-2025-29927/README.zh-cn.md

我们用浏览器插件看看

发现是next.js框架的,且版本为15.2.2,符合漏洞版本!那么接下来我们开始漏洞复现。
这是一个需要登录的界面。
然后我们用hackbar加一个请求头即可。
1 | x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware |

若我们没有加请求头,访问http://192.168.79.128:3000/是跳转到/login的,但是我们我们加了这个请求头,就会跳转到/dashboard,实现了鉴权绕过!
这个漏洞的复现是非常简单的,接下来我们开始漏洞的源码分析。
漏洞分析
关键点自然在于请求头x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
在 Next.js 中,中间件(Middleware)是一种强大的工具,可以用于在请求到达 API 路由或页面组件之前执行全局逻辑,比如身份验证、请求拦截、重定向等。
Next.js 的中间件(Middleware)设计用于处理全局请求逻辑(如身份验证、请求拦截,假设开发者编写了一个身份验证中间件,其功能是:当用户访问受保护资源时,中间件需向校验 API 发送请求以验证用户权限(如查询数据库或调用外部服务)。
由于 Next.js 是「全栈框架」,所有请求(包括中间件内部发起的 API 请求)均会再次经过中间件本身。这导致以下递归流程:
1 | 用户请求 → 中间件A(身份验证) → 发起API请求 → 中间件A再次执行 → 再次发起API请求 → ... |
最终引发无限递归,导致服务崩溃或响应超时。
为打破递归循环,Next.js 引入了两项机制:
- 请求标记: 中间件内部发起的请求会自动添加
x-middleware-subrequest
头,其值按层级递增(如middleware:middleware:middleware
)。 - 递归终止条件: 当检测到
x-middleware-subrequest
头中包含当前中间件标识(如middleware
)的次数超过阈值(默认5次),中间件直接返回空响应,终止递归
主要逻辑如下:next.js-15.2.2\packages\next\src\server\web\sandbox
1 | export const run = withTaggedErrors(async function runWithTaggedErrors(params) { |
这里解释一下这段代码,将x-middleware-subrequest请求头赋值给subreq,再以:
分割为数组subrequests。
若subrequests中包含当前中间件的名字,那么执行acc+1,也就是递增。当acc的值>=5时,中间件返回空相应,设置返回头为’x-middleware-next’: ‘1’,表示忽略中间件的所有逻辑(包括鉴权检查)。
所以漏洞的利用思路是,只要在发送给next.js的请求中增加一个头**x-middleware-subrequest:**,让其循环5次中间件文件路径,就可以绕过next.js的中间件。如果开发者将校验用户身份的逻辑放在中间件里,就将导致认证绕过漏洞。
旧版本(v<12.2)
主要的处理逻辑:
next.js/packages/next/server/next-server.ts
如果我们有中间件,那么就会调用 runMiddleware 函数
1 | protected async runMiddleware(params: { |
我们看一下以下代码:
1 | if (subrequests.includes(middlewareInfo.name)) { |
如果subrequests数组中包含middlewareInfo.name,那么这个中间件直接跳过,返回NextResponse.next(),请求会继续前进,完全绕过该中间件的执行。
所以我们的绕过思路就是让x-middleware-subrequest请求头包含中间件的名称,从而欺骗runMiddleware认为该中间件已经执行过,导致其被跳过。
1 | GET /dashboard/team/admin HTTP/1.1 |
那么接下来我们需要知道的是中间件的名称,只要有了中间件的名称那么我们就可以实现鉴权绕过了。
首先中间件必须命名为 _middleware.ts
必须位于 pages/ 目录或其子目录下(因为 App Router 在 13 版本才推出)
因此:
1 | x-middleware-subrequest: pages/_middleware |
若中间件位于 /pages/dashboard/_middleware.ts
,则 middlewareInfo.name
为 pages/dashboard/_middleware
,攻击者只需在请求头中设置 x-middleware-subrequest: pages/dashboard/_middleware
即可绕过。
新版本(v>=12.2)
新版本其实就是我们在漏洞分析中所说的:
1 | export const run = withTaggedErrors(async function runWithTaggedErrors(params) { |
那么目前就是寻找中间件名称了,而在高版本,对中间件的文件又要了要求,中间件文件更名为 middleware.ts
,位于 src/
或根目录。此时,攻击者只需设置 x-middleware-subrequest: middleware
或 x-middleware-subrequest: src/middleware
即可绕过。
漏洞修复
升级 Next.js 版本
- ≥15.2.3(15.x)
- ≥14.2.25(14.x)
直接看github上的官方的commit
https://github.com/vercel/next.js/commit/9704c8e9fcc58236349ed787903831579440a879

官方修复就是直接删掉x-middleware-subrequest请求头
参考文章:
https://zhero-web-sec.github.io/research-and-things/nextjs-and-the-corrupt-middleware