Gate 广场“新星计划”正式上线!
开启加密创作之旅,瓜分月度 $10,000 奖励!
参与资格:从未在 Gate 广场发帖,或连续 7 天未发帖的创作者
立即报名:https://www.gate.com/questionnaire/7396
您将获得:
💰 1,000 USDT 月度创作奖池 + 首帖 $50 仓位体验券
🔥 半月度「爆款王」:Gate 50U 精美周边
⭐ 月度前 10「新星英雄榜」+ 粉丝达标榜单 + 精选帖曝光扶持
加入 Gate 广场,赢奖励 ,拿流量,建立个人影响力!
详情:https://www.gate.com/announcements/article/49672
Next.js 与中间件漏洞:失效的授权机制
撰文:Rachid.A
翻译:Yewlne
01 译文正文
最近,我与化名为 inzo_ 的 Yasser Allam 决定联手开展研究。在讨论了多个潜在目标后,我们决定将焦点放在 Next.js 上(在 GitHub 上拥有 13 万 Stars,目前每周下载量超过 940 万次)。这是一个我非常熟悉的框架,与它有美好的创作经历,正如我之前的研究成果所证明的那样。因此,本文中的「我们」自然指代我们两人。
Next.js 是一个基于 React 的全功能 JavaScript 框架,拥有丰富的特性——是深入研究细节的理想场所。怀着信念、好奇与韧性,我们踏上旅程,探索那些鲜为人知的角落,寻找藏匿其中的宝藏。
不久之后,我们就在中间件中发现了一个重大问题。其影响范围广泛,所有版本均受影响,且利用此漏洞无需任何前置条件——我们将很快进行详细展示。
目录
Next.js 中间件
授权神器:宝藏级老代码
执行顺序与 middlewareInfo.name
授权神器:昨日已成诗,今朝更值得
/src 目录
最大递归深度
漏洞利用
绕过授权/重写
绕过 CSP
通过缓存投毒实现 DoS(What?)
澄清
安全公告 - CVE-2025-29927
免责声明
结语
Next.js 中间件
中间件允许你在请求完成之前执行代码。然后,你可以根据传入的请求,通过重写、重定向、修改请求或响应头,或直接返回响应的方式来修改响应内容(摘自 Next.js 文档)。
作为一个完整的框架,Next.js 拥有自己的中间件(middleware)——这是一个重要且被广泛使用的特性。它的应用场景众多,其中最重要的包括:
路径重写(Path rewriting)
服务器端重定向(Server-side redirects)
向响应添加头信息(如 CSP 等)元素
最重要的是:身份验证(Authentication)和授权(Authorization)
中间件的一个常见用途是进行授权,这涉及基于特定条件来保护特定路径。
身份验证和授权:在授予对特定页面或 API 路由的访问权限之前,确保用户身份并检查会话 Cookie(Next.js 文档)。
示例:当用户尝试访问 /dashboard/admin 时,请求首先会通过中间件,中间件会检查用户的会话 cookie 是否有效以及是否具有必要的权限。如果验证通过,中间件便转发请求;否则,中间件会将用户重定向到登录页面:
授权神器:宝藏级老代码
正如一位伟人曾经说过的,「talk is cheap, show me the bug」,让我们避免过多的叙述,直接切入主题;我们在浏览框架的旧版本(v12.0.7)时,我们发现了这段代码:
当 Next.js 应用程序使用中间件时,会调用 runMiddleware 函数。除了其主要功能外,该函数还会获取 x-middleware-subrequest 头部的值,并用它来判断是否应该应用中间件。该头部值会使用冒号(:)作为分隔符被拆分成列表,然后检查这个列表是否包含 middlewareInfo.name 值。这意味着,如果我们在请求中添加带有正确值的 x-middleware-subrequest 头部,那么中间件——无论其用途如何——将被完全忽略,请求将通过 NextResponse.next() 被转发,并且将完成到原始目的地的路径,而不受中间件的任何影响。这个头部及其值就像一把「万能钥匙」,可以绕过所有规则。此时我们已经意识到发现了一个惊人的问题,接下来需要完成最后几个拼图。
要让我们的「万能钥匙」生效,它的值必须包含 middlewareInfo.name,但这个值究竟是什么呢?
执行顺序与 middlewareInfo.name
middlewareInfo.name 的值非常容易推测,它仅仅是中间件所在的路径。要了解这一点,我们需要简单了解一下中间件在旧版本中的配置方式。
首先,在 12.2 版本之前——这个版本中中间件约定发生了变化——文件必须命名为 _middleware.ts。此外,app 路由器(router)仅在 Next.js 的版本 13 中才引入。当时存在的唯一路由器是 pages 路由器,因此该文件必须放在 pages 文件夹内(特定于路由器)。
有了这些信息,我们就能推断出中间件的确切路径,从而猜测 x-middleware-subrequest 头部的值。这个值只是由目录名称(即当时存在的唯一路由器名称)和文件名组成,遵循当时以下划线开头的命名约定:
x-middleware-subrequest: pages/_middleware
当我们尝试绕过那些被配置为系统性地将访问尝试从 /dashboard/team/admin 重定向到 /dashboard 的中间件时:
成功了,我们侵入了 ⚔️
我们现在可以完全绕过中间件,从而绕过任何基于它的保护系统,最典型的就是授权,就像我们上面的例子。这个发现相当惊人,但还有其他需要考虑的点。
12.2 之前的版本允许嵌套路由在目录树的任何位置(从pages文件夹开始)放置一个或多个_middleware文件,并且它们有执行顺序,正如我们在从Web Archive中检索到的旧文档截图中所看到的:
的,这对我们的漏洞利用意味着什么?
可能性 = 路径中的层级数量
因此,要访问/dashboard/panel/admin(受中间件保护),middlewareInfo.name的值有三种可能性,相应地x-middleware-subrequest的值也有三种可能性:
pages/_middleware
或
pages/dashboard/_middleware
或
pages/dashboard/panel/_middleware
授权神器:昨日已成诗,今朝更值得
到目前为止,我们认为只有版本 13 之前的版本容易受到攻击,因为中间件已在源代码中被移动,并且我们还没有覆盖它的所有方面。我们推测维护者一定已经注意到了这个漏洞,并在版本 13 的重大更改之前修复了它,所以我们向框架维护者报告了这个漏洞并继续了我们的研究。
令我们大为惊讶的是,在最初发现后两天,我们发现所有版本的 Next.js——从版本 11.1.4 开始——都存在漏洞! 代码不再位于同一位置,漏洞利用的逻辑也略有变化。
如前所述,从版本 12.2 开始,文件不再包含下划线,必须简单命名为 middleware.ts。此外,它不再位于 pages 文件夹中(这对我们来说很方便,因为从版本 13 开始,引入了 app 路由器,这本会使可能性加倍)。
With that in mind, the payload for the first versions starting with version 12.2 is very simple:
考虑到这一点,从版本 12.2 开始的第一个版本的有效负载非常简单:
x-middleware-subrequest: middleware
/src 目录
还需要考虑到 Next.js 提供了创建 /src 目录的可能性:
(Next.js documentation) 作为在项目根目录中拥有特殊 Next.js app 或 pages 目录的替代方法,Next.js 还支持将应用程序代码放在 src 目录下的常见模式。(Next.js 文档)
在这种情况下,payload 将是:
x-middleware-subrequest: src/middleware
因此,无论路径中有多少层级,总共只有两种可能性。这简化了针对相关版本的漏洞利用难度。
在最新版本中,它又有了一点变化(我们保证,最后一次)。
最大递归深度
在更新的版本中,逻辑又略有变化,请看这段代码:
v15.1.7
和之前一样,系统会检索 x-middleware-subrequest 头部的值,并使用冒号作为分隔符形成一个列表。但这次,请求直接转发的条件——即忽略中间件规则——有所不同:
常量depth的值必须大于或等于常量 MAX_RECURSION_DEPTH 的值(即 5)。在赋值过程中,每当列表 subrequests(即由:分隔的头部值)中的某个值等于 params.name(即中间件的路径)时,常量depth就会增加 1。如前所述,这里只有两种可能性:middleware 或 src/middleware。
因此,为了绕过中间件,我们只需要在请求中添加以下头部/值:
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
或
x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware
这段代码最初是用来做什么的?
这段代码似乎是为了防止递归请求陷入无限循环。
漏洞利用
既然我们知道您喜欢这类内容,这里有一些来自 Bug Bounty Program 的真实案例。
绕过授权/重写
在这个例子中,当我们尝试访问/admin/login时,收到404响应。从响应头中可以看出,中间件执行了路径重写,以防止未经授权或不适当的用户访问:
但使用我们的授权神器 :
我们可以毫无障碍地访问该端点,中间件被完全忽略。目标 Next.js 版本:15.1.7
绕过 CSP
这次网站使用中间件来设置——除了其他功能外——CSP和cookie:
让我们绕过它:
Target next.js version: 15.0.3Target next.js 版本:15.0.3
注意: 请留意两个目标的payload差异,其中一个使用了src/目录,而另一个没有。
通过缓存投毒实现 DoS(What?)
是的,通过这个漏洞也可能实现缓存投毒 DoS 攻击。这显然不是我们首先要寻找的,但如果没有敏感路径受到保护,且没有更有趣的可利用点,那么某些情况可能会导致缓存投毒拒绝服务(CPDoS):
假设一个网站根据用户地理位置重写用户路径,添加(/en、/fr等),且没有在根路径(/)上提供页面或资源。如果我们绕过中间件,就会避开重写,最终到达根页面。由于开发者并未打算让用户访问根页面,因此没有提供相应页面,我们会得到404(或根据重写配置/类型不同,可能是500)。
如果该网站使用了缓存/CDN 系统,可能会强制缓存404响应,导致页面不可用,严重影响站点可用性。
澄清
自安全公告发布以来,我们收到了一些人的咨询,他们担心自己的应用程序安全,并且不太理解攻击的范围。需要明确的是,易受攻击的元素是中间件。如果您没有使用中间件(或至少没有将其用于敏感目的),那么无需担心(不过,请检查上面提到的 DoS 方面),因为绕过中间件不会绕过任何实际的安全机制。
否则,后果可能是灾难性的,我们建议您迅速实施安全公告中的指导措施。
安全公告 - CVE-2025-29927
补丁
对于 Next.js 15.x,此问题已在 15.2.3 中修复
对于 Next.js 14.x,此问题已在 14.2.25 中修复
对于 Next.js 版本 11.1.4 到 13.5.6,我们建议查阅以下解决方法。
解决方案
如果无法升级到安全版本,我们建议你阻止包含 x-middleware-subrequest 请求头的外部用户请求访问你的 Next.js 应用。
严重性
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N(严重程度:9.1/10,临界级)
更多信息
在撰写本文时,部署在 Vercel 和 Netlify 上的应用显然已不再受此漏洞影响(更新:由于存在大量误报,Cloudflare 已将该规则调整为仅在用户主动启用时生效——这些误报未能有效区分来自合法用户的请求与潜在攻击者的请求)。
免责声明
本研究发布仅用于教育目的,旨在帮助开发者理解问题的根本原因,或为研究人员 / 漏洞猎人在未来的研究工作中提供启发。本文作为安全公告的补充材料,提供了有关漏洞本质的进一步说明和澄清——因为公告中已公开了导致该漏洞的请求头(以及相关的提交差异)。
我们明确声明不支持对本文进行任何不道德的使用。
结语
正如本文所强调的,这个漏洞在 Next.js 源代码中已经存在了数年,随着中间件及其版本的演变而变化。任何软件都可能出现严重漏洞,但当它影响到最流行的框架之一时,就变得特别危险,可能对更广泛的生态系统造成严重后果。如前所述,在撰写本文时,Next.js 每周下载量接近 1000 万次。它广泛应用于从银行服务到区块链等关键领域。当漏洞影响到用户依赖的成熟功能(如授权和身份验证)时,风险就更大了。
Vercel团队花了几天时间来解决这个漏洞,但值得注意的是,一旦他们意识到问题,修复就被提交、合并到几小时内实现到新版本中(包括向后移植)。
时间线 :
2025 年 2 月 27 日:向维护者报告了漏洞(当时我们认为仅 12.0.0 至 12.0.7 版本受影响,并在报告中注明了这一点)。
2025 年 3 月 1 日:发送了第二封邮件,说明实际上所有版本都存在漏洞,包括最新稳定版本。
2025 年 3 月 5 日:收到 Vercel 团队的初步回复,表示 12.x 版本已不再维护(可能尚未阅读我们在第二封邮件中附带的安全公告模板,未注意到所有版本均受影响的情况)。
2025 年 3 月 5 日:再次发送邮件,请团队尽快查阅第二封邮件及安全公告模板。
2025 年 3 月 11 日:再次发送邮件,确认新信息是否已被采纳。
2025 年 3 月 17 日:收到 Vercel 团队的回复,确认已采纳相关信息。
2025 年 3 月 18 日:收到 Vercel 团队的邮件,表示报告已被接受,修复补丁已完成。数小时后发布了包含修复的版本 15.2.3(并包含回溯修复)。
2025 年 3 月 21 日:安全公告正式发布。
总的来说,寻找零日漏洞的过程,只有在发现线索的那一刻才令人兴奋、肾上腺素飙升;其余时间则像是一段充满不确定性的旅程——对好奇者而言,它能带来知识上的收获;对缺乏耐心的人来说,这段旅程则显得格外漫长。别犹豫,组队行动总比独自穿越沙漠要轻松得多。