# 機能設計書 103-CSRF保護

## 概要

本ドキュメントは、Next.jsのCSRF（Cross-Site Request Forgery）保護機能の設計を記述する。Server Actionsへのクロスサイトリクエストフォージェリ攻撃を防止するためのOriginヘッダー検証機構である。

### 本機能の処理概要

リクエストのOriginヘッダーを検証し、許可されたオリジンからのリクエストのみを受け付けることで、CSRF攻撃を防止する機能である。ワイルドカードパターンによるドメインマッチングをサポートし、Server Actionsの実行時にオリジン検証を行う。

**業務上の目的・背景**：Server Actionsはフォーム送信やデータ変更を直接サーバーで処理するため、クロスサイトからの不正なリクエストを防ぐ必要がある。CSRF保護は、悪意のあるサイトからの偽装リクエストを検出・拒否することでアプリケーションのセキュリティを担保する。

**機能の利用シーン**：Server Actionsが実行される際に自動的に呼び出される。開発者は`next.config.js`の`allowedOrigins`設定で追加のオリジンを許可できる。

**主要な処理内容**：
1. リクエストのOriginヘッダーからドメインを抽出
2. 許可されたオリジンリスト（設定 + デフォルト）との照合
3. ワイルドカードパターン（`*`単一セグメント、`**`再帰マッチ）によるドメインマッチング
4. DNS名の大文字小文字を正規化した比較（RFC 1035準拠）

**関連システム・外部連携**：Server Actions実行エンジン（action-handler.ts）から呼び出される。クロスサイトアクセスブロック機能（No.106）でも同じ`isCsrfOriginAllowed`関数が再利用される。

**権限による制御**：`next.config.js`の`experimental.allowedOrigins`設定で許可オリジンを制御可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | フォーム画面 | 主画面 | Server Actionsを呼び出すフォーム送信時にCSRF検証が実行される |

## 機能種別

セキュリティ / バリデーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| originDomain | string | Yes | リクエストのOriginヘッダーから抽出されたドメイン名 | - |
| allowedOrigins | string[] | No | 許可されたオリジンドメインのリスト（ワイルドカード対応） | デフォルトは空配列 |

### 入力データソース

- HTTPリクエストのOriginヘッダー
- `next.config.js`のallowedOrigins設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | boolean | オリジンが許可されている場合true |

### 出力先

呼び出し元関数への戻り値

## 処理フロー

### 処理シーケンス

```
1. isCsrfOriginAllowed関数が呼び出される
   └─ originDomainとallowedOriginsを受け取る
2. originDomainをASCII小文字正規化
   └─ RFC 1035準拠のケース非依存比較
3. allowedOriginsを順に走査
   └─ 空文字列のパターンはスキップ
4. 完全一致チェック
   └─ 正規化後のオリジンとパターンを比較
5. ワイルドカードマッチング（matchWildcardDomain）
   └─ パターンをドット区切りで分解し、末尾から比較
```

### フローチャート

```mermaid
flowchart TD
    A[isCsrfOriginAllowed] --> B[originDomain正規化]
    B --> C[allowedOriginsループ]
    C --> D{allowedOriginが空?}
    D -->|Yes| C
    D -->|No| E[allowedOrigin正規化]
    E --> F{完全一致?}
    F -->|Yes| G[true返却]
    F -->|No| H[matchWildcardDomain]
    H --> I{マッチ?}
    I -->|Yes| G
    I -->|No| C
    C -->|全て不一致| J[false返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-103-1 | DNS名ケース非依存 | DNS名はRFC 1035に基づきASCII小文字に正規化して比較 | すべてのオリジン比較 |
| BR-103-2 | ワイルドカード `*` | 単一セグメントの任意の値にマッチ（非空必須） | パターンに`*`を含む場合 |
| BR-103-3 | ワイルドカード `**` | 先頭に配置した場合のみ有効で、1つ以上のサブドメインにマッチ | パターン先頭が`**`の場合 |
| BR-103-4 | 安全なワイルドカード | `*`や`**`単独（パターン長1）はドメイン全体へのマッチを防止するため不許可 | patternPartsの長さが1の場合 |
| BR-103-5 | 空パターン不許可 | 空文字列のパターンセグメントは無効として不一致 | パターンセグメントが空の場合 |
| BR-103-6 | `**`の位置制約 | `**`はパターンの先頭（最後にpopされる位置）にのみ許可 | patternPartsに`**`が途中にある場合 |

### 計算ロジック

ワイルドカードマッチングアルゴリズム:
1. ドメインとパターンをドットで分割
2. 末尾のセグメントから順に比較（popベース）
3. `*`は任意の1セグメントにマッチ
4. `**`は残りの全セグメント（1つ以上）にマッチ（先頭パターンのみ有効）
5. すべてのパターンセグメントを消費し、ドメインセグメントも消費された場合のみマッチ

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

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | オリジン不一致 | リクエストのOriginが許可リストに含まれない場合 | falseを返却し、呼び出し元でリクエストを拒否 |

### リトライ仕様

リトライは不要。

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

該当なし。

## パフォーマンス要件

- ワイルドカードマッチングは文字列比較のみで実行され、正規表現を使用しないため高速
- `Array.some`による短絡評価で最初のマッチ発見時に即座に終了

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

- ASCII小文字正規化にはUnicode問題を避けるため`replace(/[A-Z]/g, ...)`を使用（`toLowerCase()`ではなく）
- `*`や`**`単独でのドメイン全体マッチは安全性のため明示的に拒否
- `**`が途中のセグメントに配置された場合は無効とすることで予測不能なマッチを防止

## 備考

- 本機能はEdge Runtimeでも実行可能。micromatchはEdgeで使用できないため、簡易的なワイルドカード実装を採用している
- 関連する`remotePatterns`の仕様に準拠したワイルドカードパターンを使用

---

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

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

### 推奨読解順序

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

本機能は型定義が少なく、string型の引数と戻り値のみで構成される。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | csrf-protection.ts | `packages/next/src/server/app-render/csrf-protection.ts` | isCsrfOriginAllowed関数のシグネチャ（72-74行目） |

**読解のコツ**: 関数は純粋関数として設計されており、外部状態に依存しない。テスト容易性が高い。

#### Step 2: ワイルドカードマッチングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | csrf-protection.ts | `packages/next/src/server/app-render/csrf-protection.ts` | matchWildcardDomain関数（6-70行目） |

**主要処理フロー**:
1. **9-10行目**: ドメインとパターンのASCII小文字正規化
2. **12-13行目**: ドット区切りでの分割
3. **15-18行目**: パターンが空の場合の無効判定
4. **20-23行目**: ドメインセグメント数がパターンより少ない場合の不一致判定
5. **27-32行目**: `*`や`**`単独パターンの安全拒否
6. **34-66行目**: 末尾からのセグメント比較ループ
7. **69行目**: すべてのセグメントが消費されたかの最終判定

#### Step 3: 公開API関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | csrf-protection.ts | `packages/next/src/server/app-render/csrf-protection.ts` | isCsrfOriginAllowed関数（72-94行目） |

**主要処理フロー**:
- **78-79行目**: originDomainのASCII小文字正規化
- **82-93行目**: allowedOriginsをsomeで走査し、完全一致またはワイルドカードマッチを確認

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

```
Server Actions Handler (action-handler.ts)
    │
    └─ isCsrfOriginAllowed(originDomain, allowedOrigins)
           │
           └─ matchWildcardDomain(originDomain, pattern)
                  ├─ 正規化（ASCII toLowerCase）
                  ├─ セグメント分割
                  └─ 末尾からのマッチングループ

Block Cross-Site (block-cross-site.ts)
    │
    └─ isCsrfOriginAllowed(originLowerCase, allowedOrigins)
```

### データフロー図

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

Origin Header ──────────────> isCsrfOriginAllowed() ──────> boolean
  (ドメイン抽出済み)               │
                                  ├─ ASCII正規化
allowedOrigins[] ──────────>      ├─ 完全一致チェック
  (next.config.js設定)            └─ matchWildcardDomain()
                                       ├─ セグメント分割
                                       └─ パターンマッチ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| csrf-protection.ts | `packages/next/src/server/app-render/csrf-protection.ts` | ソース | CSRF保護のメイン実装（ワイルドカードマッチング含む） |
| block-cross-site.ts | `packages/next/src/server/lib/router-utils/block-cross-site.ts` | ソース | 開発時クロスサイトアクセスブロック（isCsrfOriginAllowedを再利用） |
| action-handler.ts | `packages/next/src/server/app-render/action-handler.ts` | ソース | Server Actions実行時のCSRF検証呼び出し元 |
| config-shared.ts | `packages/next/src/server/config-shared.ts` | ソース | allowedOrigins設定の型定義 |
