# Next.js 与中间件漏洞：失效的授权机制

By [LXDAO](https://paragraph.com/@lxdao) · 2025-04-07

---

原作：Rachid.A

原文：

[https://zhero-web-sec.github.io/research-and-things/nextjs-and-the-corrupt-middleware](https://zhero-web-sec.github.io/research-and-things/nextjs-and-the-corrupt-middleware)

本期文章由 LXDAO 成员 Yewlne 翻译。

译文正文 最近，我与化名为 inzo\_ 的 Yasser Allam 决定联手开展研究。在讨论了多个潜在目标后，我们决定将焦点放在 Next.js

[https://github.com/vercel/next.js](https://github.com/vercel/next.js)

GitHub 上拥有 13 万 Stars，目前每周下载量超过 940 万次）。这是一个我非常熟悉的框架，与它有美好的创作经历，正如我之前的研究成果

[https://zhero-web-sec.github.io/research-and-things/](https://zhero-web-sec.github.io/research-and-things/)

所证明的那样。因此，本文中的"我们"自然指代我们两人。

Next.js 是一个基于 React 的全功能 JavaScript 框架，拥有丰富的特性 — — 是深入研究细节的理想场所。怀着信念、好奇与韧性，我们踏上旅程，探索那些鲜为人知的角落，寻找藏匿其中的宝藏。

不久之后，我们就在中间件中发现了一个重大问题。其影响范围广泛，所有版本均受影响，且利用此漏洞无需任何前置条件 — — 我们将很快进行详细展示。

目录
--

*   **Next.js 中间件**
    
*   **授权神器：宝藏级老代码**
    
    *   **执行顺序与** [**middlewareInfo.name**](http://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 是否有效以及是否具有必要的权限。如果验证通过，中间件便转发请求；否则，中间件会将用户重定向到登录页面：

![](https://storage.googleapis.com/papyrus_images/5a8f17ecdbbbaa38e86f0e0c271f0a936731c74331ce7d8ec26c80b2e8d2c300.png)

### 授权神器：宝藏级老代码

正如一位伟人曾经说过的，”talk is cheap, show me the bug”，让我们避免过多的叙述，直接切入主题；我们在浏览框架的旧版本（v12.0.7）时，我们发现了这段代码

[https://github.com/vercel/next.js/blob/v12.0.7/packages/next/server/next-server.ts](https://github.com/vercel/next.js/blob/v12.0.7/packages/next/server/next-server.ts)

当 Next.js 应用程序使用中间件时，会调用 runMiddleware 函数。除了其主要功能外，该函数还会获取 x-middleware-subrequest 头部的值，并用它来判断是否应该应用中间件。该头部值会使用冒号（:）作为分隔符被拆分成列表，然后检查这个列表是否包含 [middlewareInfo.name](http://middlewareInfo.name) 值。这意味着，如果我们在请求中添加带有正确值的 x-middleware-subrequest 头部，那么中间件 — — 无论其用途如何 — — 将被完全忽略，请求将通过 NextResponse.next() 被转发，并且将完成到原始目的地的路径，而不受中间件的任何影响。这个头部及其值就像一把”万能钥匙”，可以绕过所有规则。此时我们已经意识到发现了一个惊人的问题，接下来需要完成最后几个拼图。

要让我们的”万能钥匙”生效，它的值必须包含 [middlewareInfo.name](http://middlewareInfo.name)，但这个值究竟是什么呢？

执行顺序与 [middlewareInfo.name](http://middlewareInfo.name) [middlewareInfo.name](http://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 的中间件时：

![](https://storage.googleapis.com/papyrus_images/9cbc3d56727c96d0347447cf39b12a44ef8cca5f7a923ecfb04e511865fa1652.png)

成功了，我们侵入了 ⚔️

我们现在可以完全绕过中间件，从而绕过任何基于它的保护系统，最典型的就是授权，就像我们上面的例子。这个发现相当惊人，但还有其他需要考虑的点。

12.2 之前的版本允许嵌套路由在目录树的任何位置（从pages文件夹开始）放置一个或多个\_middleware文件，并且它们有执行顺序，正如我们在从Web Archive中检索到的旧文档截图中所看到的：

![](https://storage.googleapis.com/papyrus_images/cc82e51fead1c80f78b618f66e5090beafd29b8e7b8a06345392783e2b479bff.png)

**这对我们的漏洞利用意味着什么？**

可能性 = 路径中的层级数量

因此，要访问/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
    

因此，**无论路径中有多少层级，总共只有两种可能性**。这简化了针对相关版本的漏洞利用难度。

在最新版本中，它又有了一点变化（我们保证，最后一次）。

### 最大递归深度

在更新的版本中，逻辑又略有变化，请看这段代码：

![](https://storage.googleapis.com/papyrus_images/1874c3361cd3210bdf7a62d041460f6e55becb58f67010508aac156336f9a2a4.png)

v15.1.7

和之前一样，系统会检索 x-middleware-subrequest 头部的值，并使用冒号作为分隔符形成一个列表。但这次，请求直接转发的条件 — — 即忽略中间件规则 — — 有所不同：

常量depth的值必须大于或等于常量 MAX\_RECURSION\_DEPTH 的值（即 5）。在赋值过程中，每当列表 subrequests（即由:分隔的头部值）中的某个值等于 [params.name](http://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
    

**这段代码最初是用来做什么的？**

这段代码似乎是为了防止递归请求陷入无限循环。

[https://nextjs.org/blog/cve-2025-29927](https://nextjs.org/blog/cve-2025-29927)

### 漏洞利用

既然我们知道您喜欢这类内容，这里有一些来自 Bug Bounty Program 的真实案例。

**绕过授权/重写**

在这个例子中，当我们尝试访问/admin/login时，收到404响应。从响应头中可以看出，中间件执行了路径重写，以防止未经授权或不适当的用户访问：

![](https://storage.googleapis.com/papyrus_images/9f758951f2059b16ffae6babf495f83ab602d2360cd45b1e359b65ed1e7999b0.png)

但使用我们的授权神器 ：

![](https://storage.googleapis.com/papyrus_images/976ef0884e3bb948fdc4f513a28ab90e65708fcffec9b9e17469bb9b9653e914.png)

我们可以毫无障碍地访问该端点，中间件被完全忽略。目标 Next.js 版本：15.1.7

**绕过 CSP**

这次网站使用中间件来设置 — — 除了其他功能外 — — CSP和cookie：

![](https://storage.googleapis.com/papyrus_images/d6809126be6706b22a3de6eb98d506bdf1572aaa60bb2ad3d0e958be12a2aa4b.png)

让我们绕过它：

![](https://storage.googleapis.com/papyrus_images/4ecd90a441f5473f26df6cf788ad37f751fe061187f7af5b312739ebb697127d.png)

Target next.js version: 15.0.3Target next.js 版本：15.0.3

**注意**： 请留意两个目标的payload差异，其中一个使用了src/目录，而另一个没有。

**通过缓存投毒实现 DoS（What？）**

是的，通过这个漏洞也可能实现缓存投毒 DoS 攻击。这显然不是我们首先要寻找的，但如果没有敏感路径受到保护，且没有更有趣的可利用点，那么某些情况可能会导致缓存投毒拒绝服务（CPDoS）：

假设一个网站根据用户地理位置重写用户路径，添加（/en、/fr等），且没有在根路径（/）上提供页面或资源。如果我们绕过中间件，就会避开重写，最终到达根页面。由于开发者并未打算让用户访问根页面，因此没有提供相应页面，我们会得到404（或根据重写配置/类型不同，可能是500）。

如果该网站使用了缓存/CDN 系统，可能会强制缓存404响应，导致页面不可用，**严重影响站点可用性**。

![](https://storage.googleapis.com/papyrus_images/fdfe0a3d9d0b67eeadaa62ebe7eac7f98b8e660efced2228ec5dea2c7fa6743e.png)

**澄清**

自安全公告发布以来，我们收到了一些人的咨询，他们担心自己的应用程序安全，并且不太理解攻击的范围。需要明确的是，易受攻击的元素是中间件。如果您没有使用中间件（或至少没有将其用于敏感目的），那么无需担心（不过，请检查上面提到的 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，临界级）

[https://github.com/vercel/next.js/security/advisories/GHSA-f82v-jwr5-mffw](https://github.com/vercel/next.js/security/advisories/GHSA-f82v-jwr5-mffw)

### 更多信息

在撰写本文时，部署在 Vercel 和 Netlify 上的应用显然已不再受此漏洞影响（更新：由于存在大量误报，Cloudflare 已将该规则调整为仅在用户主动启用时生效 — — 这些误报未能有效区分来自合法用户的请求与潜在攻击者的请求）。

[https://x.com/nextjs/status/1903522002431857063](https://x.com/nextjs/status/1903522002431857063)

免责声明
----

本研究发布仅用于教育目的，旨在帮助开发者理解问题的根本原因，或为研究人员 / 漏洞猎人在未来的研究工作中提供启发。本文作为安全公告的补充材料，提供了有关漏洞本质的进一步说明和澄清 — — 因为公告中已公开了导致该漏洞的请求头（以及相关的提交差异）。

我们明确声明不支持对本文进行任何不道德的使用。

结语
--

正如本文所强调的，这个漏洞在 Next.js 源代码中已经存在了数年，随着中间件及其版本的演变而变化。任何软件都可能出现严重漏洞，但当它影响到最流行的框架之一时，就变得特别危险，可能对更广泛的生态系统造成严重后果。如前所述，在撰写本文时，Next.js 每周下载量接近 1000 万次。它广泛应用于从银行服务到区块链等关键领域。当漏洞影响到用户依赖的成熟功能（如授权和身份验证）时，风险就更大了。

Vercel 团队花了几天时间来解决这个漏洞，但值得注意的是，一旦他们意识到问题，修复就被提交、合并到几小时内实现到新版本中（包括向后移植）。

编译 | Yewlne

编辑 & 排版 | Yewlne、环环

---

*Originally published on [LXDAO](https://paragraph.com/@lxdao/next-js)*
