# 機能設計書 106-クロスサイトアクセスブロック

## 概要

本ドキュメントは、Next.jsの開発サーバーにおけるクロスサイトアクセスブロック機能の設計を記述する。異なるオリジンからの`/_next/*`や`/__nextjs`リソースへの不正アクセスを検出・ブロックする仕組みである。

### 本機能の処理概要

開発サーバーにおいて、内部リソース（`/_next/*`および`/__nextjs`エンドポイント）への異なるオリジンからのリクエストを検出し、設定に応じて警告またはブロックする機能である。`sec-fetch-mode`/`sec-fetch-site`ヘッダーおよびOriginヘッダーを検査して判定する。

**業務上の目的・背景**：開発サーバーが外部サイトのscriptタグなどから不正にアクセスされることを防止するセキュリティ機能である。特に、開発サーバーのHMR WebSocket接続やNext.js内部APIへの意図しないクロスサイトアクセスは、開発環境のセキュリティリスクとなる。

**機能の利用シーン**：`next dev`で開発サーバーを起動した際に自動的に有効化される。`next.config.js`の`allowedDevOrigins`設定で追加の許可オリジンを指定できる。設定が明示的に行われていない場合は警告モード、設定されている場合はブロックモードで動作する。

**主要な処理内容**：
1. リクエストURLが内部エンドポイント（`/_next/*`、`/__nextjs`）であるかの判定
2. 画像リクエスト（`/_next/image`）と静的メディア（`/_next/static/media`）の除外
3. `sec-fetch-mode: no-cors` + `sec-fetch-site: cross-site`の検出（scriptタグ経由の埋め込み）
4. Originヘッダーの検証（CSRF保護関数を再利用）
5. 警告モード（warnOnce）またはブロックモード（403レスポンス）の選択

**関連システム・外部連携**：CSRF保護機能（No.103）の`isCsrfOriginAllowed`関数を再利用する。Router Serverから呼び出される。

**権限による制御**：`next.config.js`の`allowedDevOrigins`配列で許可オリジンを制御。localhost（`*.localhost`、`localhost`）はデフォルトで許可。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 開発サーバーコンソール | 主出力先 | 警告メッセージまたはブロックログの出力先 |

## 機能種別

セキュリティ / アクセス制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| req | IncomingMessage | Yes | HTTPリクエストオブジェクト | - |
| res | ServerResponse \| Duplex | Yes | HTTPレスポンスまたはWebSocket Duplexオブジェクト | - |
| allowedDevOrigins | string[] \| undefined | No | 許可するオリジンのリスト | 未定義の場合は警告モード |
| hostname | string \| undefined | No | 開発サーバーのホスト名 | - |

### 入力データソース

- HTTPリクエストヘッダー（`sec-fetch-mode`、`sec-fetch-site`、`origin`）
- `next.config.js`の`allowedDevOrigins`設定
- 開発サーバーのホスト名

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| blocked | boolean | リクエストがブロックされた場合true |

### 出力先

- ブロック時：403レスポンス（body: "Unauthorized"）
- 警告時：コンソール警告（`warnOnce`経由）
- 非ブロック時：戻り値false（処理続行）

## 処理フロー

### 処理シーケンス

```
1. blockCrossSite関数が呼び出される
   └─ Router Serverのリクエスト処理開始時に呼び出される
2. モード判定
   └─ allowedDevOriginsがundefined → 警告モード、定義済み → ブロックモード
3. 許可オリジンリストの構築
   └─ デフォルト（*.localhost, localhost） + allowedDevOrigins + hostname
4. 内部エンドポイント判定
   └─ isInternalDevEndpoint: /_next/*(image/media除外) or /__nextjs
5. non-CORSクロスサイトリクエスト検出
   └─ sec-fetch-mode: no-cors AND sec-fetch-site: cross-site
6. Originヘッダー検証
   └─ isCsrfOriginAllowed（CSRF保護関数を再利用）
7. 警告またはブロック応答
   └─ warnOrBlockRequestで適切なアクションを実行
```

### フローチャート

```mermaid
flowchart TD
    A[blockCrossSite] --> B{内部エンドポイント?}
    B -->|No| C[false返却 - 非対象]
    B -->|Yes| D{no-cors + cross-site?}
    D -->|Yes| E[warnOrBlockRequest]
    D -->|No| F{Originヘッダーあり?}
    F -->|No| C2[false返却]
    F -->|Yes| G[URLパース]
    G --> H{isCsrfOriginAllowed?}
    H -->|Yes| I[false返却 - 許可]
    H -->|No| J[warnOrBlockRequest]
    E --> K{モード}
    J --> K
    K -->|warn| L[warnOnce + false返却]
    K -->|block| M[403レスポンス + true返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-106-1 | デフォルト許可 | `*.localhost`と`localhost`はデフォルトで許可 | 常時 |
| BR-106-2 | ホスト名許可 | 開発サーバーのhostnameは自動的に許可リストに追加 | hostnameが指定されている場合 |
| BR-106-3 | 画像除外 | `/_next/image`リクエストはクロスサイトチェック対象外 | URL判定時 |
| BR-106-4 | 静的メディア除外 | `/_next/static/media`リクエストはクロスサイトチェック対象外（CSSからのロード対応） | URL判定時 |
| BR-106-5 | 警告モード | `allowedDevOrigins`未設定時はブロックせず警告のみ | allowedDevOriginsがundefined |
| BR-106-6 | ブロックモード | `allowedDevOrigins`設定済みの場合、不一致で403を返す | allowedDevOriginsが定義済み |
| BR-106-7 | Origin null許容 | Originヘッダーが`null`文字列の場合はチェックをスキップ | rawOrigin === 'null' |

### 計算ロジック

特になし。

## データベース操作仕様

### 操作別データベース影響一覧

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | ブロックモードでオリジンが不許可 | レスポンスに"Unauthorized"を返却 |
| - | URLパースエラー | 不正なOriginヘッダー | try-catchでisInternalDevEndpoint内のエラーを捕捉しfalse返却 |

### リトライ仕様

リトライは不要。

## トランザクション仕様

該当なし。

## パフォーマンス要件

- 各リクエストに対して実行されるため、高速な判定が求められる
- `warnOnce`による重複警告の抑制でコンソール出力を最小化

## セキュリティ考慮事項

- 開発モード専用の機能であり、本番サーバーでは動作しない
- `sec-fetch-mode`/`sec-fetch-site`ヘッダーはブラウザが自動的に付与するため、偽装が困難
- 将来のメジャーバージョンでは、`allowedDevOrigins`未設定時もブロックモードに移行予定

## 備考

- `warnOrBlockRequest`関数はHTTPレスポンス（ServerResponse）とWebSocketアップグレード（Duplex）の両方に対応している
- `parseUrl`はlibディレクトリのURL解析ユーティリティを使用

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

本機能はNode.jsの標準的なHTTPオブジェクトを使用する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | block-cross-site.ts | `packages/next/src/server/lib/router-utils/block-cross-site.ts` | IncomingMessage、ServerResponse、Duplex型の使用 |

**読解のコツ**: `Duplex`型はWebSocketアップグレード時のストリームに使用される。`'statusCode' in res`でHTTPレスポンスかDuplexかを判別している。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | block-cross-site.ts | `packages/next/src/server/lib/router-utils/block-cross-site.ts` | blockCrossSite関数（53-101行目） |

**主要処理フロー**:
1. **61行目**: `allowedDevOrigins`の有無でwarn/blockモードを決定
2. **63-70行目**: 許可オリジンリストの構築（デフォルト + 設定 + hostname）
3. **73-75行目**: `isInternalDevEndpoint`で対象URLの判定
4. **78-83行目**: `sec-fetch-mode`/`sec-fetch-site`ヘッダーによるno-CORSクロスサイト検出
5. **86-98行目**: Originヘッダーの検証と`isCsrfOriginAllowed`呼び出し

#### Step 3: 内部エンドポイント判定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | block-cross-site.ts | `packages/next/src/server/lib/router-utils/block-cross-site.ts` | isInternalDevEndpoint関数（34-51行目） |

**主要処理フロー**:
- **39行目**: `/__nextjs`を含むミドルウェアリクエストの検出
- **40行目**: `/_next`を含む内部アセットリクエストの検出
- **43-45行目**: `/_next/image`と`/_next/static/media`の除外

#### Step 4: 警告/ブロック処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | block-cross-site.ts | `packages/next/src/server/lib/router-utils/block-cross-site.ts` | warnOrBlockRequest関数（7-32行目） |

**主要処理フロー**:
- **13-18行目**: 警告モード：`warnOnce`で次期バージョンでの変更を通知
- **20-31行目**: ブロックモード：403ステータスコード設定と"Unauthorized"レスポンス

### プログラム呼び出し階層図

```
Router Server (router-server.ts)
    │
    └─ blockCrossSite(req, res, allowedDevOrigins, hostname)
           │
           ├─ isInternalDevEndpoint(req)
           │      └─ URL文字列判定
           │
           ├─ warnOrBlockRequest(res, origin, mode)
           │      ├─ warnOnce() [警告モード]
           │      └─ res.statusCode = 403 [ブロックモード]
           │
           ├─ parseUrl(rawOrigin) [lib/url]
           │
           └─ isCsrfOriginAllowed(hostname, allowedOrigins)
                  └─ matchWildcardDomain() [csrf-protection.ts]
```

### データフロー図

```
[入力]                            [処理]                       [出力]

IncomingMessage ──────────────> blockCrossSite() ────────────> boolean
  ├─ url                            │
  ├─ headers.sec-fetch-mode         ├─ URL判定
  ├─ headers.sec-fetch-site         ├─ ヘッダー検査
  └─ headers.origin                 └─ Origin検証

allowedDevOrigins[] ──────────> 許可リスト構築
hostname ─────────────────────> allowedOrigins[]

ServerResponse/Duplex ────────> warnOrBlockRequest() ────────> 403 or warning
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| block-cross-site.ts | `packages/next/src/server/lib/router-utils/block-cross-site.ts` | ソース | クロスサイトアクセスブロックのメイン実装 |
| csrf-protection.ts | `packages/next/src/server/app-render/csrf-protection.ts` | ソース | isCsrfOriginAllowed関数（オリジン検証の再利用） |
| url.ts | `packages/next/src/lib/url.ts` | ソース | parseUrl関数（URLパースユーティリティ） |
| log.ts | `packages/next/src/build/output/log.ts` | ソース | warnOnce関数（重複警告抑制） |
| router-server.ts | `packages/next/src/server/lib/router-server.ts` | ソース | blockCrossSiteの呼び出し元 |
