(번역) Next.js와 훼손된 미들웨어: 인가 아티팩트

한정(Han Jung)
15 min read3 days ago

원문: https://zhero-web-sec.github.io/research-and-things/nextjs-and-the-corrupt-middleware

소개

최근에 inzo_라는 가명으로 알려진 Yasser Allam과 저는 함께 연구하게 되었습니다. 저희는 잠재적인 문제가 있는 대상에 대해 논의했고 Next.js(GitHub에서 130K 스타, 현재 주당 940만 회 이상 다운로드)에 초점을 맞추기로 했습니다. Next.js는 잘 아는 프레임워크로, 이전 작업에서도 좋은 결과를 낸 바 있습니다. 따라서 이 글에서 “저희”는 자연스럽게 우리 두 사람을 지칭할 것입니다.

Next.js는 리액트를 기반으로 한 포괄적인 자바스크립트 프레임워크로, 수많은 기능을 포함하고 있어 연구의 복잡성을 파헤치기에 완벽한 장소라고 생각했습니다. 저희는 믿음과 호기심, 그리고 끈기를 바탕으로 발견되기를 기다리는 숨겨진 보물들을 찾아 Next.js의 잘 알려지지 않은 부분들을 탐색하기 시작했습니다.

얼마 지나지 않아 저희는 미들웨어에서 중대한 발견을 하게 되었습니다. 그 영향은 상당히 크며, 모든 버전이 영향을 받고, 악용 가능성에 대한 전제 조건이 없었습니다. 곧 소개해 드리겠습니다.

목차

  • Next.js 미들웨어
  • 인가 아티팩트: 오래된 코드, 오래된 보물
    • 실행 순서와 middlewareInfo.name
  • 인가 아티팩트: 과거를 그리워하는 것도 좋지만, 현재를 살아가는 것이 더 나은 법이다
    • /src 디렉터리
    • 최대 재귀 깊이
  • 악용 사례
    • 인가/재작성 우회
    • CSP 우회
    • 캐시 포이즈닝을 통한 DoS (정말요?)
    •명확한 설명
  • 보안 권고 — CVE-2025–29927
  • 면책 조항
  • 결론

Next.js 미들웨어

미들웨어를 사용하면 요청이 완료되기 전에 코드를 실행할 수 있습니다. 그런 다음 들어오는 요청을 기반으로 재작성, 리다이렉트, 요청 또는 응답 헤더 수정 또는 직접 응답함으로써 응답을 수정할 수 있습니다(Next.js 문서).

Next.js는 완전한 프레임워크로서 자체 미들웨어를 갖고 있습니다. 이는 중요하고 널리 사용되는 기능입니다. 사용 사례는 다양하지만, 가장 중요한 것들은 다음과 같습니다.

  • 경로 재작성
  • 서버 측 리다이렉트
  • 응답에 헤더(CSP 등)와 같은 요소 추가
  • 그리고 가장 중요한 인증 및 권한 부여

미들웨어의 일반적인 사용 사례는 권한 부여로, 특정 조건에 따라 특정 경로를 보호하는 것을 포함합니다.

인증 및 권한 부여: 특정 페이지나 API 경로에 대한 접근을 허용하기 전에 사용자 신원을 확인하고 세션 쿠키를 검사합니다(Next.js 문서).

예시: 사용자가 /dashboard/admin에 접근하려고 할 때, 요청은 먼저 미들웨어를 통과하게 됩니다. 미들웨어는 사용자의 세션 쿠키가 유효한지 확인하고 필요한 권한을 부여합니다. 만약 유효하다면, 미들웨어는 요청을 전달할 것입니다. 그렇지 않다면, 미들웨어는 사용자를 로그인 페이지로 리다이렉트할 것입니다.

인가 아티팩트: 오래된 코드, 오래된 보물

위대한 사람이 한때 말했듯이, “말은 쉽지만, 버그를 보여달라.” 불필요한 이야기는 피하고 바로 요점으로 들어가겠습니다. 프레임워크의 이전 버전(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 라우터는 Next.js의 버전 13에서야 출시되었습니다. 당시에 존재했던 유일한 라우터는 pages 라우터였으므로, 파일은 pages 폴더(라우터 특정) 내에 위치해야 했습니다.

이 정보를 통해 미들웨어의 정확한 경로를 추론할 수 있으며, 따라서 x-middleware-subrequest 헤더의 값을 추측할 수 있습니다. 이 값은 단순히 디렉터리의 이름(당시 존재했던 유일한 라우터의 이름)과 당시 규칙에 따라 언더스코어로 시작하는 파일 이름으로 구성됩니다.

x-middleware-subrequest: pages/_middleware

그리고 /dashboard/team/admin에 대한 접근 시도를 체계적으로 /dashboard로 리다이렉트 하도록 구성된 미들웨어를 우회하려고 할 때,

아키, 우리는 해냈다 ⚔️

이제 저희는 미들웨어를 완전히 우회할 수 있으므로, 위 예시와 같은 권한 부여를 비롯한 미들웨어 기반의 모든 보호 시스템을 우회할 수 있습니다. 꽤 놀라운 일이지만, 고려해야 할 다른 사항들도 있습니다.

12.2 버전 이전에는 중첩된 라우트에서 pages 폴더부터 시작하여 트리 내 어디에든 하나 이상의 _middleware 파일을 배치할 수 있었으며, 실행 순서가 있었습니다. 이는 오래된 웹 아카이브에서 가져온 이전 문서의 스크린숏에서 볼 수 있습니다.

그렇다면, 이것이 우리의 악용 방법에 어떤 의미가 있을까요?

가능성 = 경로의 레벨 수

따라서, (미들웨어로 보호된) /dashboard/panel/admin에 접근하기 위해서는 middlewareInfo.name, 즉 x-middleware-subrequest의 값에 관한 세 가지 가능성이 있습니다.

pages/_middleware

또는

pages/dashboard/_middleware

또는

pages/dashboard/panel/_middleware

인가 아티팩트: 과거를 그리워하는 것도 좋지만, 현재를 살아가는 것이 더 나은 법이다

지금까지 저희는 미들웨어가 소스 코드에서 이동되었고 아직 일부 영역을 다루지 않았기 때문에 버전 13 이전의 버전만 취약하다고 생각했습니다. 저희는 프레임워크 메인테이너들이 버전 13에서 이루어진 주요 변경 사항 전에 취약점을 발견하고 수정했을 것이라고 생각하여 취약점을 보고하고 연구를 계속했습니다.

놀랍게도, 초기 발견 이후 이틀 만에 저희는 버전 11.1.4부터 시작하는 모든 Next.js 버전이 취약하다는 사실을 알게 되었습니다! 코드는 더 이상 같은 위치에 있지 않으며, 악용 로직이 약간 변경되었습니다.

이전에 설명한 대로, 버전 12.2부터는 파일에 더 이상 밑줄이 포함되지 않으며 단순히 middleware.ts로 명명되어야 합니다. 또한, 더 이상 pages 폴더에 위치해서는 안 됩니다 (이는 버전 13부터 app 라우터가 도입되었기 때문에 우리에게 편리한데, 그렇지 않으면 가능성의 수가 두 배로 늘어났을 것입니다).

이를 염두에 두고, 버전 12.2부터 시작하는 첫 번째 버전들에 대한 페이로드는 매우 간단합니다.

x-middleware-subrequest: middleware

/src 디렉터리

Next.js가 /src 디렉터리를 생성할 가능성을 제공한다는 점도 고려해야 합니다.

프로젝트 루트에 특별한 Next.js app 또는 pages 디렉터리를 두는 대안으로, Next.js는 src 디렉터리 아래에 애플리케이션 코드를 배치하는 일반적인 패턴도 지원합니다. (Next.js 문서)

이 경우 페이로드는 다음과 같을 것입니다.

x-middleware-subrequest: src/middleware

따라서, 경로의 레벨 수와 관계없이 총 두 가지 가능성이 있습니다. 이는 관련된 몇몇 버전에 대한 악용을 단순화합니다. 최신 버전에서는 다시 약간 변경됩니다. (마지막일 겁니다)

최대 재귀 깊이

최신 버전에서는 로직이 다시 약간 변경되었습니다. 다음 코드 조각을 살펴보세요.

v15.1.7

이전과 마찬가지로 x-middleware-subrequest 헤더의 값을 검색하여 콜론 문자를 구분자로 하는 목록을 형성합니다. 하지만 이번에는 요청이 직접 전달되어 미들웨어의 규칙을 무시하는 조건이 다릅니다.

depth 값은 MAX_RECURSION_DEPTH 상수(기본값은 5)보다 이상이어야 합니다. depth는 할당될 때, 목록 subrequests의 값 중 하나(헤더 값을 :로 분리한 결과)가 단순히 미들웨어 경로인 params.name 값과 동일할 때마다 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

이 코드가 원래 무엇에 사용되는 건가요?

이것은 재귀적 요청이 무한 루프에 빠지는 것을 방지하기 위한 것으로 보입니다.

악용 사례

여러분이 이런 것을 좋아하신다는 것을 알기에, BBP 프로그램의 실제 예시를 몇 가지 소개해 보겠습니다.

인가/재작성 우회

여기서 /admin/login에 접근하려고 하면 404 오류가 발생합니다. 응답 헤더에서 볼 수 있듯이, 인가되지 않은/부적절한 사용자가 접근하지 못하도록 미들웨어를 통해 경로 재작성이 수행됩니다.

하지만 우리의 인가 아티팩트를 사용하면,

미들웨어가 완전히 무시되어 아무런 문제 없이 엔드포인트에 접근할 수 있습니다. 대상 Next.js 버전: 15.1.7

CSP 우회

이번에는 사이트가 미들웨어를 사용하여 여러 가지 중에서도 CSP와 쿠키를 설정합니다.

이를 우회해 봅시다.

대상 Next.js 버전: 15.0.3

참고: 두 대상 간의 페이로드 차이를 주목하세요. 하나는 src/ 디렉터리를 사용했고, 다른 하나는 사용하지 않았습니다.

캐시 포이즈닝을 통한 DoS (정말요?)

네, 이 취약점으로 인해 캐시 포이즈닝 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) https://github.com/vercel/next.js/security/advisories/GHSA-f82v-jwr5-mffw

추가 정보

이 글을 작성하는 시점에서 Vercel과 Netlify의 애플리케이션은 더 이상 취약하지 않은 것으로 보입니다(업데이트: Cloudflare는 정당한 사용자의 요청과 잠재적 공격자의 요청을 구분하지 못하는 다수의 오탐지로 인해 이 규칙을 옵트인 전용으로 변경했습니다)(링크).

https://x.com/nextjs/status/1903522002431857063

면책 조항

이 연구 발표는 교육 목적으로만 제공됩니다. 개발자들이 문제의 근본 원인을 이해하거나 연구자/버그 헌터들이 향후 활동에서 영감을 얻는 데 도움을 주기 위함입니다. 이 발표는 보안 권고와 함께 제공되며, 취약점을 유발하는 헤더((커밋 차이점도 마찬가지))가 이미 공개된 바와 같이 그 특성에 관한 명확한 설명을 제공합니다.

저희는 당연히 이 글의 비윤리적 사용에 대해 어떠한 책임도 지지 않습니다.

결론

이 문서에서 강조했듯이, 이 취약점은 여러 해 동안 Next.js 소스 코드에 존재했으며, 미들웨어와 버전에 따른 변화에 맞춰 진화해 왔습니다. 어떤 소프트웨어에서도 심각한 취약점이 발생할 수 있지만, 가장 인기 있는 프레임워크 중 하나에 영향을 미칠 때 특히 위험해지며 더 넓은 생태계에 심각한 결과를 초래할 수 있습니다. 앞서 언급했듯이, 글을 작성하는 시점에서 Next.js는 주당 거의 1천만 번 다운로드되고 있습니다. 은행 서비스부터 블록체인까지 중요한 분야에서 널리 사용되고 있습니다. 취약점이 권한 부여 및 인증 등의 필수 기능을 위해 사용자가 의존하는 성숙한 기능에 영향을 미칠 때 위험은 더욱 커집니다.

이 취약점은 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일: 보안 권고 발표

마무리하자면, 제로데이 취약점 탐색은 단서가 나타날 때는 흥미롭고 아드레날린이 분비되지만 그 외의 시간에는 불확실한 여정입니다. 호기심 많은 사람들에게는 지식적으로 보람차고, 인내심이 부족한 사람들에게는 비교적 지루하게 느껴집니다. 팀을 이루는 것을 주저하지 마세요. 사막은 함께 건널때 더 쉽습니다.

더 많은 연구가 진행 중이니, 계속 연결되고 싶으시면 저희의 각 X 계정을 팔로우하세요.

읽어주셔서 감사합니다.

Al hamduliLlah; zhero; & inzo_

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

한정(Han Jung)
한정(Han Jung)

Written by 한정(Han Jung)

개인용 블로그로 사용하고 있습니다. 좋은 개발자가 꿈입니다. > https://www.notion.so/Han-Jung-c43f4bcd2b3f4b3d85b93aee41c5e098

No responses yet

Write a response