NextJS
Reading time: 22 minutes
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
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/: 애플리케이션의 페이지, 레이아웃, 구성 요소 및 API 경로를 위한 중앙 디렉토리입니다. App Router 패러다임을 채택하여 고급 라우팅 기능과 서버-클라이언트 구성 요소 분리를 가능하게 합니다.
- 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 모듈을 포함합니다.
- app/utils/: 애플리케이션 전반에서 공유할 수 있는 유틸리티 함수, 헬퍼 모듈 및 기타 비 UI 로직을 포함합니다.
- .env.local: 로컬 개발 환경에 특정한 환경 변수를 저장합니다. 이러한 변수는 버전 관리에 커밋되지 않습니다.
- next.config.js: webpack 구성, 환경 변수 및 보안 설정을 포함하여 Next.js 동작을 사용자 정의합니다.
- tsconfig.json: 프로젝트에 대한 TypeScript 설정을 구성하여 타입 검사 및 기타 TypeScript 기능을 활성화합니다.
- package.json: 프로젝트 종속성, 스크립트 및 메타데이터를 관리합니다.
- README.md: 프로젝트에 대한 문서 및 정보를 제공하며, 설정 지침, 사용 가이드라인 및 기타 관련 세부 정보를 포함합니다.
- yarn.lock / package-lock.json: 프로젝트의 종속성을 특정 버전으로 고정하여 다양한 환경에서 일관된 설치를 보장합니다.
Next.js의 클라이언트 측
app
디렉토리의 파일 기반 라우팅
app
디렉토리는 최신 Next.js 버전에서 라우팅의 초석입니다. 파일 시스템을 활용하여 경로를 정의하여 라우트 관리를 직관적이고 확장 가능하게 만듭니다.
루트 경로 / 처리
파일 구조:
my-nextjs-app/
├── app/
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── next.config.js
└── ...
주요 파일:
app/page.tsx
: 루트 경로/
에 대한 요청을 처리합니다.app/layout.tsx
: 모든 페이지를 감싸는 애플리케이션의 레이아웃을 정의합니다.
구현:
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>
);
}
설명:
- 경로 정의:
app
디렉토리 바로 아래의page.tsx
파일은/
경로에 해당합니다. - 렌더링: 이 컴포넌트는 홈 페이지의 내용을 렌더링합니다.
- 레이아웃 통합:
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>
)
}
설명:
- 경로 정의:
about
폴더 안의page.tsx
파일은/about
경로에 해당합니다. - 렌더링: 이 컴포넌트는 about 페이지의 내용을 렌더링합니다.
동적 경로
동적 경로는 변수 세그먼트가 있는 경로를 처리할 수 있게 하여, 애플리케이션이 ID, 슬러그 등과 같은 매개변수에 따라 내용을 표시할 수 있게 합니다.
예시: /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
경로
파일 구조:
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 경로를 반영하여 유지 관리성과 명확성을 향상시킵니다.
모든 경로 처리
모든 경로 처리는 여러 중첩 세그먼트 또는 알 수 없는 경로를 처리하여 경로 처리의 유연성을 제공합니다.
예시: /*
경로
파일 구조:
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 세그먼트:
[...slug]
는 모든 남은 경로 세그먼트를 배열로 캡처합니다. - 사용법: 사용자 생성 경로, 중첩 카테고리 등과 같은 동적 라우팅 시나리오를 처리하는 데 유용합니다.
- 경로 일치:
/anything/here
,/foo/bar/baz
와 같은 경로는 이 구성 요소에 의해 처리됩니다.
잠재적인 클라이언트 측 취약점
Next.js는 안전한 기반을 제공하지만, 부적절한 코딩 관행은 취약점을 도입할 수 있습니다. 주요 클라이언트 측 취약점은 다음과 같습니다:
교차 사이트 스크립팅 (XSS)
XSS 공격은 악성 스크립트가 신뢰할 수 있는 웹사이트에 주입될 때 발생합니다. 공격자는 사용자의 브라우저에서 스크립트를 실행하여 데이터를 훔치거나 사용자를 대신하여 작업을 수행할 수 있습니다.
취약한 코드의 예:
// Dangerous: Injecting user input directly into HTML
function Comment({ userInput }) {
return <div dangerouslySetInnerHTML={{ __html: userInput }} />
}
왜 취약한가: 신뢰할 수 없는 입력과 함께 dangerouslySetInnerHTML
을 사용하면 공격자가 악성 스크립트를 주입할 수 있습니다.
클라이언트 측 템플릿 주입
사용자 입력이 템플릿에서 부적절하게 처리될 때 발생하며, 공격자가 템플릿이나 표현식을 주입하고 실행할 수 있게 합니다.
취약한 코드 예시:
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
에 악의적인 내용이 포함되면 의도하지 않은 코드 실행으로 이어질 수 있습니다.
클라이언트 경로 탐색
이는 공격자가 클라이언트 측 경로를 조작하여 Cross-Site Request Forgery (CSRF)와 같은 의도하지 않은 작업을 수행할 수 있게 하는 취약점입니다. 서버 측 경로 탐색이 서버의 파일 시스템을 목표로 하는 것과 달리, 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>
)
}
공격 시나리오
- 공격자의 목표:
filePath
를 조작하여 중요한 파일(예:admin/config.json
)을 삭제하기 위한 CSRF 공격 수행. - CSPT 악용:
- 악의적인 입력: 공격자는
filePath
를 조작한 URL을 생성합니다. 예:../deleteFile/config.json
. - 결과 API 호출: 클라이언트 측 코드가
/api/files/../deleteFile/config.json
에 요청을 보냅니다. - 서버의 처리: 서버가
filePath
를 검증하지 않으면 요청을 처리하여 민감한 파일을 삭제하거나 노출할 수 있습니다.
- CSRF 실행:
- 조작된 링크: 공격자는 피해자에게 링크를 보내거나 조작된
filePath
로 다운로드 요청을 트리거하는 악성 스크립트를 삽입합니다. - 결과: 피해자는 알지 못하게 작업을 실행하여 무단 파일 접근 또는 삭제로 이어집니다.
왜 취약한가
- 입력 검증 부족: 클라이언트 측에서 임의의
filePath
입력을 허용하여 경로 탐색이 가능하게 합니다. - 클라이언트 입력 신뢰: 서버 측 API가
filePath
를 신뢰하고 정화 없이 처리합니다. - 잠재적 API 작업: API 엔드포인트가 상태 변경 작업(예: 파일 삭제, 수정)을 수행하는 경우 CSPT를 통해 악용될 수 있습니다.
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
Serverless Functions (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 경로는
app/api/
디렉토리 아래에 배치됩니다. - 파일 명명: 각 API 엔드포인트는
route.js
또는route.ts
파일이 포함된 자체 폴더에 위치합니다. - 내보낸 함수: 단일 기본 내보내기 대신 특정 HTTP 메서드 함수(예:
GET
,POST
)가 내보내집니다. - 응답 처리:
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" },
})
}
설명:
- 다중 내보내기: 각 HTTP 메서드(
GET
,PUT
,DELETE
)는 고유한 내보내기 함수를 가지고 있습니다. - 매개변수: 두 번째 인수는
params
를 통해 경로 매개변수에 접근할 수 있습니다. - 향상된 응답: 응답 객체에 대한 더 큰 제어를 제공하여 정확한 헤더 및 상태 코드 관리를 가능하게 합니다.
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" },
})
}
설명:
- 구문:
[...]
는 모든 중첩 경로를 포착하는 포괄적인 세그먼트를 나타냅니다. - 용도: 다양한 경로 깊이나 동적 세그먼트를 처리해야 하는 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`);
}
}
모범 사례:
- 관심사의 분리: 서로 다른 HTTP 메서드에 대한 로직을 명확하게 분리합니다.
- 응답 일관성: 클라이언트 측 처리를 용이하게 하기 위해 일관된 응답 구조를 보장합니다.
- 오류 처리: 지원되지 않는 메서드와 예기치 않은 오류를 우아하게 처리합니다.
CORS 구성
어떤 출처가 API 경로에 접근할 수 있는지 제어하여 교차 출처 리소스 공유(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는 또한 middleware.ts
파일 내의 모든 API 경로에서 구성할 수 있습니다:
// 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
클라이언트 측의 서버 코드 노출
서버에서 사용되는 코드를 클라이언트 측에서 노출되고 사용되는 코드에서도 쉽게 사용할 수 있습니다. 클라이언트 측에서 파일이 노출되지 않도록 보장하는 가장 좋은 방법은 파일의 시작 부분에 이 import를 사용하는 것입니다:
import "server-only"
주요 파일 및 역할
middleware.ts
/ middleware.js
위치: 프로젝트의 루트 또는 src/
내.
목적: 요청이 처리되기 전에 서버 측 서버리스 함수에서 코드를 실행하여 인증, 리디렉션 또는 응답 수정과 같은 작업을 수행할 수 있게 함.
실행 흐름:
- 수신 요청: 미들웨어가 요청을 가로챔.
- 처리: 요청에 따라 작업 수행 (예: 인증 확인).
- 응답 수정: 응답을 변경하거나 다음 핸들러로 제어를 전달할 수 있음.
예시 사용 사례:
- 인증되지 않은 사용자 리디렉션.
- 사용자 정의 헤더 추가.
- 요청 로깅.
샘플 구성:
// 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*"],
}
next.config.js
위치: 프로젝트의 루트.
목적: Next.js 동작을 구성하고, 기능을 활성화 또는 비활성화하며, webpack 구성을 사용자 정의하고, 환경 변수를 설정하고, 여러 보안 기능을 구성합니다.
주요 보안 구성:
보안 헤더
보안 헤더는 브라우저에 콘텐츠를 처리하는 방법을 지시하여 애플리케이션의 보안을 강화합니다. 이들은 교차 사이트 스크립팅(XSS), 클릭재킹, MIME 타입 스니핑과 같은 다양한 공격을 완화하는 데 도움을 줍니다:
- 콘텐츠 보안 정책 (CSP)
- X-Frame-Options
- X-Content-Type-Options
- 엄격한 전송 보안 (HSTS)
- 리퍼러 정책
예시:
// 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 키 및 데이터베이스 자격 증명과 같은 민감한 정보를 클라이언트에 노출하지 않고 안전하게 관리합니다.
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, 데이터베이스 또는 기타 서비스에 대한 무단 접근을 얻을 수 있습니다.
리다이렉트
응용 프로그램 내에서 URL 리다이렉션 및 재작성 관리를 통해 사용자가 적절하게 안내되도록 하여 열린 리다이렉트 취약점이 발생하지 않도록 합니다.
a. 열린 리다이렉트 취약점
잘못된 구성 예:
// next.config.js
module.exports = {
async redirects() {
return [
{
source: "/redirect",
destination: (req) => req.query.url, // Dynamically redirects based on query parameter
permanent: false,
},
]
},
}
문제:
- 동적 목적지: 사용자가 임의의 URL을 지정할 수 있어 오픈 리디렉션 공격을 가능하게 합니다.
- 사용자 입력 신뢰: 검증 없이 사용자가 제공한 URL로 리디렉션하면 피싱, 악성 소프트웨어 배포 또는 자격 증명 도용으로 이어질 수 있습니다.
공격자가 이를 악용하는 방법:
공격자는 귀하의 도메인에서 시작된 것처럼 보이는 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
},
}
문제:
- 민감한 경로 노출: 민감한 디렉토리를 별칭 처리하고 클라이언트 측 접근을 허용하면 기밀 정보가 유출될 수 있습니다.
- 비밀 번들링: 민감한 파일이 클라이언트를 위해 번들링되면, 그 내용이 소스 맵이나 클라이언트 측 코드를 검사하여 접근할 수 있게 됩니다.
공격자가 이를 악용하는 방법:
공격자는 애플리케이션의 디렉토리 구조에 접근하거나 재구성할 수 있으며, 잠재적으로 민감한 파일이나 데이터를 찾고 악용할 수 있습니다.
pages/_app.js
및 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
},
}
참고: 변수를 서버 측 전용으로 제한하려면 env
객체에서 생략하거나 클라이언트 노출을 위해 NEXT_PUBLIC_
로 접두사를 붙이십시오.
인증 및 권한 부여
접근 방식:
- 세션 기반 인증: 쿠키를 사용하여 사용자 세션을 관리합니다.
- 토큰 기반 인증: 상태 비저장 인증을 위해 JWT를 구현합니다.
- 타사 제공업체:
next-auth
와 같은 라이브러리를 사용하여 OAuth 제공업체(예: Google, GitHub)와 통합합니다.
보안 관행:
- 보안 쿠키:
HttpOnly
,Secure
, 및SameSite
속성을 설정합니다. - 비밀번호 해싱: 비밀번호를 저장하기 전에 항상 해시합니다.
- 입력 검증: 입력을 검증하고 정리하여 주입 공격을 방지합니다.
예시:
// 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
컴포넌트를 사용하여 자동 이미지 최적화를 수행합니다. - 코드 분할: 동적 임포트를 활용하여 코드를 분할하고 초기 로드 시간을 줄입니다.
- 캐싱: API 응답 및 정적 자산에 대한 캐싱 전략을 구현합니다.
- 지연 로딩: 필요할 때만 컴포넌트나 자산을 로드합니다.
예시:
// Dynamic Import with Code Splitting
import dynamic from "next/dynamic"
const HeavyComponent = dynamic(() => import("../components/HeavyComponent"), {
loading: () => <p>Loading...</p>,
})
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.