CVE-2025-29927 漏洞分析:当 Next.js 的防死循环机制,变成了中间件鉴权绕过的入口
前文本文通过CVE-2025-29927来叙述Next.js框架的严重Critical漏洞这个漏洞的核心是中间件Middleware鉴权绕过。它并不是业务代码本身直接出现了认证缺陷而是Next.js内部为了防止中间件递归调用而设计的子请求标记机制被外部请求伪造后触发了“跳过 Middleware”的错误路径。漏洞概述CVE编号 CVE-2025-29927漏洞类型鉴权绕过可以跳过身份认证和授权受影响产品* Next.js 15.x 15.2.3Next…js 14.x 14.2.25Next.js 13.x 13.5.9Next.js 12.x 12.3.5补充说明官方还要求 11.x 用户采用临时缓解措施该分支未提供常规补丁版本漏洞位置中间件的路由防死循环验证漏洞表现 攻击者可以通过构造特定的 HTTP 请求头完全跳过 Next.js 的中间件逻辑从而绕过身份验证Authentication和授权Authorization。漏洞载体Next.js Middleware中间件Middleware是什么在Next.js中Middleware是一段请求完成并响应之前运行的代码通常定义在 middleware.ts 或 middleware.js 中。它是充当客户端和后端路由/页面之间的“守门员”。它的主要作用是什么Middleware 的设计初衷是为了在全局层面集中处理非业务逻辑减少核心业务代码的冗余。它通常被用于实现以下目标统一鉴权与访问控制最核心 检查用户的 Token、Cookie 或 Session判断其是否有权限访问某些受保护的路由如 /admin 或 /dashboard。如果没有权限则将其重定向到登录页。国际化i18n与本地化路由 根据用户的地理位置或语言偏好重定向到特定的语言版本如从 / 重定向到 /en-US。安全性增强 注入安全相关的 HTTP 头如 CSP 策略。日志记录与限流 在请求到达具体页面前记录访问日志或进行速率限制Rate Limiting。防死循环机制设计Next.js的内部路由机制比较复杂当请求到达中间件时会涉及它的请求重写Rewrite和内部路由跳转。在这个过程中Next.js 会发起“内部的子请求”。正常重写逻辑Next.js 的 Middleware 运行在缓存与路由处理之前开发者通常会在这里实现鉴权、重写、重定向等逻辑。假设用户要访问 tenant-a.example.com/dashboard中间件在第一次接收到这个请求后可能会把它重写为内部的实际路径 /app/tenant-a/dashboard也就是沟通前端 URL 与后端实际服务器返回内容之间的桥梁。在这个过程中Next.js 会把这个重写后的目标重新交给内部路由系统去处理而为了避免这种内部跳转反复再次进入中间件、最终导致递归失控框架就引入了专门的防死循环机制。死循环的发生如果因为开发者的失误导致中间件的逻辑出现缺陷没有排除已经重写过的路径会发生一个悲剧第一次匹配用户请求 /api/data -中间件重写为 /api/v2/data第二次匹配内部子请求/api/v2/data再次被中间件重写为/api/v2/v2/data。。。。。。如果没有一个“刹车机制”这种内部的相互调用会瞬间耗尽服务器Node.js 进程的内存和 CPU导致服务直接崩溃OOM - Out of Memory 或栈溢出。妥协机制为了防止开发者导致的惨剧Next.js在每次发起内部子请求时强行向字段中加注一个x-middleware-subrequest 计数器。它会记录这个子请求被重写的次数如果超过5次那么框架会认为“开发者编写出现问题为了不让进程死掉我需要停止让中间件处理这个请求把当前的请求直接丢给后端渲染”。这个“为了保命而强行放行”的底层防御逻辑把它变成了攻破大门的钥匙。漏洞原理在实际框架中中间件通过NextResponse.rewrite() 或 NextResponse.next() 触发内部子请求。为了防止中间件出发递归的无限死循环Next.js 引入了 MAX_RECURSION_DEPTH最大递归深度默认通常为 5和 x-middleware-subrequest 请求头来作为计数器。以下是存在漏洞的内部逻辑模拟// 模拟 Next.js 内部路由调度器 (存在漏洞的版本)constMAX_RECURSION_DEPTH5;functionhandleRequest(req,res){// 1. 获取请求头中的递归深度标记// 正常情况下这个 header 是 Next.js 内部发起的子请求自带的constsubrequestHeaderreq.headers[x-middleware-subrequest];// 2. 计算当前中间件的调用层级letcurrentDepth0;if(subrequestHeader){// Next.js 会通过冒号 : 将 header 值分割计算长度作为当前深度// 例如 middleware:middleware 的深度就是 2currentDepthsubrequestHeader.split(:).length;}// 3. 【致命缺陷点】如果深度达到或超过上限为了防止死循环框架会强行终止中间件if(currentDepthMAX_RECURSION_DEPTH){console.warn(Middleware recursion limit reached, skipping middleware...);// 直接进入页面渲染逻辑中间件鉴权被完全跳过returnrenderProtectedPage(req,res);}// 4. 正常执行中间件逻辑 (如验证 Token未登录则重定向)returnexecuteMiddleware(req,res);}可以看到如果真的触发死循环机制了框架会直接跳过中间件中的其他机制鉴权等等并让请求继续进入后续的路由分发、页面渲染或接口处理流程这就让当前这个请求没有认证和授权过程。如果请求对应的页面、API Route 或 Server Action 没有二次鉴权的话这就相当于我可以构建任何当前架构有的功能并且以我想要的任何身份来执行它。根本原因触发漏洞的根本原因是Next.js 盲目信任了 x-middleware-subrequest 这个请求头而没有验证它的来源。其实防止死循环的机制本身并没有太大的问题出现此漏洞的原因还是因为后端中间件处理轻信了前端的参数。如果一个外部攻击者在发起 HTTP 请求时手动在请求头中伪造了这个字段GET /admin/dashboard HTTP/1.1 Host: vulnerable-app.com x-middleware-subrequest: middleware在有缺陷的版本中中间件会完全信任这个原始请求中自带的x-middleware-subrequest: middleware字段这个原始请求在中间件眼中就是一个已经完成一次重写的请求。直接原因由于中间件轻信前端x-middleware-subrequest字段倘若我们构建一个GET /admin/dashboard HTTP/1.1 Host: vulnerable-app.com x-middleware-subrequest:middleware:middleware:middleware:middleware:middleware发送此请求会请求到达Next.js服务器内部逻辑读取 req.headers[‘x-middleware-subrequest’]。执行 .split(‘:’).length计算结果为 5。触发 currentDepth 5 的判断条件。Next.js 框架判定“这是一个内部请求且已经重写了 5 次为了防止服务器卡死立刻停止执行中间件直接输出最终页面。”/admin/dashboard 页面的敏感数据被直接返回给未授权的攻击者。(注在较早的 Next.js 12.x 版本中逻辑略有不同只需传入 x-middleware-subrequest: pages/_middleware 即可触发完全跳过。)可以看到请求直接越过中间件了什么中间件的鉴权认证统统都被甩到身后了也就是整个整个服务器的第一层大门已经被上述payload给干掉了在当时情况中流行使用 NextAuth.js (Auth.js)、Clerk 或 Supabase Auth。很多开发者的最佳实践是完全依赖中间件来保护整个后台路由。这样的话就可以通过这样一个简单的payload攻陷整个服务器了。源码Diff如何修复这个漏洞模拟Diff逻辑修复functionhandleRequest(req,res){// 【修复方案】在请求进入框架深处之前清洗来自外部的内部保留请求头// 确保 x-middleware-subrequest 只能由框架内部的 fetch/rewrite 生成if(isExternalClientRequest(req)){deletereq.headers[x-middleware-subrequest];}constsubrequestHeaderreq.headers[x-middleware-subrequest];letcurrentDepth0;if(subrequestHeader){currentDepthsubrequestHeader.split(:).length;}if(currentDepthMAX_RECURSION_DEPTH){returnrenderProtectedPage(req,res);}returnexecuteMiddleware(req,res);}修复逻辑其实根本原则是“不要信任来自外部边界的内部状态标识”。官方的修复思路本质上就是在请求进入核心路由引擎之前对请求信息进行严格清洗确保 x-middleware-subrequest 这样的内部标记只能由框架内部生成而不能被外部客户端直接带入。同时如果开发者暂时无法立即升级 Next.js 版本比较现实的缓解方式就是在应用前置层例如反向代理、网关或 WAF直接拦截所有来自外部、且携带该 Header 的请求以此模拟补丁效果。由此也可以看到这个漏洞本质非常简单道理也很直接就是服务器轻信了来自外部、可以被恶意伪造的内部状态参数。部署差异说明这个漏洞主要影响的是自托管的 Next.js 应用例如使用 next start 或 output: ‘standalone’ 部署的场景。如果应用运行在 Vercel 托管环境下官方说明默认不会受到这个漏洞影响。除此之外像 Netlify 和 Cloudflare Workers 这类与 Next.js 内部处理链路解耦较多的部署方式在当时的分析中也被认为不受该漏洞影响。这也说明了一个问题同样是 Next.js 项目不同的部署架构最终暴露出来的攻击面并不完全一样。中间件被完全绕过的灾难后果如果攻击者成功触发了防死循环机制导致中间件直接退出Skip这绝不仅仅是“没验证 Token”那么简单。在现代前端全栈架构中其后果是系统性的防线崩塌。核心业务数据裸奔这正是前文说道如果整个服务器完全依赖中间件来保护后端路由没有二次鉴权的情况下未授权的访客可以直接带着伪造的头访问任何页面包括其中敏感的个人身份信息、财务报表等等都会被完整渲染给前端。越权操作与 API 裸露Privilege Escalation如果Next.js API Routes (/api/… 或 Server Actions) 也被配置在中间件的保护范围内攻击者不仅可以看到页面还可以向受保护的接口发送POST/DELETE请求类似于直接POST到/api/admin/delete-userAPI如果缺乏深度防御就会乖乖执行删除操作。安全策略失效Security Headers Bypass优秀的中间件通常会做安全加固工作注入 Content-Security-Policy (CSP) 防止 XSS 攻击。注入 Strict-Transport-Security (HSTS)。执行 IP 黑名单拦截或速率限制Rate Limiting。一旦中间件被绕过这些响应头将全部丢失应用会暴露在 XSS、暴力破解等其他攻击的火力之下。国际化引发的缓存投毒i18n Cache Poisoning如果应用依赖中间件做语言重定向比如把根目录 / 强行重定向到 /en 或 /zh导致根目录 / 实际上是不存在实际页面的攻击者绕过中间件直接访问 /。Next.js 找不到 / 的页面组件抛出 404 或 500 错误。由于 Next.js 默认的强缓存机制或者外部 CDN如 Cloudflare, Vercel Edge的缓存这个 500 错误页面会被缓存下来。结果接下来所有正常用户的访问都会直接看到这个被缓存的错误页面造成严重的拒绝服务DoS。总结总结来说这个漏洞最可怕的地方并不只是“一个 Header 可以伪造”而是它利用了系统本身为了自保而设计出的“安全降级机制”让开发者精心构建的第一层防线直接变成了摆设。从这个角度看CVE-2025-29927 暴露出的并不是某一行代码写错了而是现代 Web 架构中一个非常危险的误区把中间件当成唯一的安全边界。一旦框架层的某个内部机制被绕过后面的页面、接口、Server Action 乃至缓存系统都会连锁暴露。这其实也引出了一个非常关键的现代 Web 安全架构理念纵深防御Defense in Depth。中间件可以作为第一层门禁但绝不能作为唯一的防线。真正稳固的系统必须在路由入口、中间件、业务控制器、敏感操作接口等多个层面分别进行校验这样即使其中一层失守也不会让整套系统瞬间门户大开。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2436960.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!