NextJS
Reading time: 37 minutes
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をサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリに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: Next.jsの動作をカスタマイズし、webpackの設定、環境変数、およびセキュリティ設定を含みます。
- 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>
)
}
説明:
- キャッチオールセグメント:
[...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
に悪意のあるコンテンツが含まれていると、意図しないコードの実行につながる可能性があります。
クライアントパストラバーサル
これは、攻撃者がクライアント側のパスを操作して、クロスサイトリクエストフォージェリ(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
サーバーレス関数 (API ルート)
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
)には独自のエクスポートされた関数があります。 - パラメータ: 2番目の引数は
params
を介してルートパラメータへのアクセスを提供します。 - 強化されたレスポンス: レスポンスオブジェクトに対するより大きな制御が可能で、正確なヘッダーとステータスコードの管理ができます。
キャッチオールとネストされたルート
Next.js 13+は、キャッチオールルートやネストされたAPIルートなどの高度なルーティング機能をサポートしており、より動的でスケーラブルなAPI構造を可能にします。
キャッチオールルートの例:
// 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は、すべてのAPIルート内の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
クライアントサイドでのサーバーコードの露出
サーバーによって使用されるコードをクライアントサイドで露出して使用することは簡単です。クライアントサイドでファイルが決して露出しないようにする最良の方法は、ファイルの最初にこのインポートを使用することです:
import "server-only"
重要なファイルとその役割
middleware.ts
/ middleware.js
場所: プロジェクトのルートまたは src/
内。
目的: リクエストが処理される前にサーバーサイドのサーバーレス関数でコードを実行し、認証、リダイレクト、またはレスポンスの変更などのタスクを可能にします。
実行フロー:
- 受信リクエスト: ミドルウェアがリクエストを intercepts します。
- 処理: リクエストに基づいて操作を実行します(例: 認証の確認)。
- レスポンスの変更: レスポンスを変更するか、次のハンドラーに制御を渡すことができます。
使用例:
- 認証されていないユーザーのリダイレクト。
- カスタムヘッダーの追加。
- リクエストのログ記録。
サンプル設定:
// 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
- Strict-Transport-Security (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)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。