# 機能設計書 105-Subresource Integrity

## 概要

本ドキュメントは、Next.jsのSubresource Integrity（SRI）機能の設計を記述する。ビルド時にスクリプトやスタイルシートのハッシュを生成し、ブラウザ側で整合性を検証する仕組みである。

### 本機能の処理概要

ビルド時にWebpackプラグインとしてすべてのアセットファイルのSRIハッシュを計算し、マニフェストファイルとして出力する。ランタイムでは、HTMLレンダリング時にscriptタグやpreinitコールにintegrity属性を付与することで、改ざんされたリソースの実行を防止する。

**業務上の目的・背景**：CDNやプロキシを経由するアセットファイルが改ざんされていないことを保証するためのセキュリティ機能である。Subresource Integrityにより、ブラウザはリソースのハッシュを検証し、一致しない場合は実行を拒否する。これにより、中間者攻撃やCDN侵害によるスクリプト改ざんを検出できる。

**機能の利用シーン**：`next.config.js`の`experimental.sri.algorithm`設定を有効化することで利用できる。本番ビルド（`next build`）時にSRIハッシュが計算され、本番サーバーでのHTML配信時にintegrity属性が付与される。

**主要な処理内容**：
1. Webpackコンパイル時にすべてのアセットのバイナリ内容を取得
2. 指定されたハッシュアルゴリズム（sha256/sha384/sha512）でハッシュを計算
3. `{algorithm}-{base64hash}`形式のSRI文字列を生成
4. SRIマニフェスト（JSON/JS）をサーバーディレクトリに出力
5. HTMLレンダリング時にマニフェストからintegrity属性を参照してscriptタグに付与

**関連システム・外部連携**：Webpackビルドパイプライン、App Renderのscript出力処理と連携する。

**権限による制御**：`next.config.js`の`experimental.sri`設定で有効化・アルゴリズム選択を制御。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | すべてのページ | 結果表示 | レンダリングされるHTMLのscriptタグにintegrity属性が付与される |

## 機能種別

セキュリティ / ビルド処理

## 入力仕様

### 入力パラメータ（ビルド時）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| algorithm | SubresourceIntegrityAlgorithm | Yes | ハッシュアルゴリズム（sha256/sha384/sha512） | 'sha256' \| 'sha384' \| 'sha512' |

### 入力パラメータ（ランタイム）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| SRIManifest | Record\<string, string\> | No | ファイル名からSRI文字列へのマッピング | - |
| files | string[] | Yes | ブートストラップスクリプトファイルリスト | - |

### 入力データソース

- Webpackコンパイル時のアセットバイナリ
- `next.config.js`の`experimental.sri.algorithm`設定

## 出力仕様

### 出力データ（ビルド時）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| SRIマニフェスト（.json） | JSON | ファイル名からSRI文字列へのマッピング |
| SRIマニフェスト（.js） | JavaScript | `self.__SUBRESOURCE_INTEGRITY_MANIFEST`グローバル変数として設定 |

### 出力データ（ランタイム）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| integrity属性 | string | scriptタグの`integrity`属性値（例: `sha256-xxxx`） |
| crossOrigin属性 | string | SRI使用時に必須の`crossorigin`属性 |

### 出力先

- ビルド時：`server/subresource-integrity-manifest.json` および `.js`
- ランタイム：HTMLレスポンスのscriptタグ

## 処理フロー

### 処理シーケンス

```
1. next.config.jsからSRI設定を読み込み
   └─ experimental.sri.algorithmを取得
2. SubresourceIntegrityPluginがWebpackに登録
   └─ compiler.hooks.makeでコンパイルフックを設定
3. コンパイル後のアセット処理
   └─ compilation.hooks.afterProcessAssetsで全アセットを走査
4. 各アセットのハッシュ計算
   ├─ asset.source.buffer()でバイナリ取得
   ├─ crypto.createHash(algorithm)
   └─ `${algorithm}-${base64hash}`形式で格納
5. マニフェストファイル出力
   ├─ server/subresource-integrity-manifest.json
   └─ server/subresource-integrity-manifest.js
6. ランタイムでの適用（required-scripts.tsx）
   ├─ SRIManifest存在確認
   ├─ ブートストラップスクリプトにintegrity属性設定
   └─ ReactDOM.preinitにintegrity設定を渡す
```

### フローチャート

```mermaid
flowchart TD
    A[next build] --> B{SRI設定あり?}
    B -->|No| C[通常ビルド]
    B -->|Yes| D[SubresourceIntegrityPlugin適用]
    D --> E[afterProcessAssetsフック]
    E --> F[全アセット走査]
    F --> G[crypto.createHash]
    G --> H[algorithm-base64hash生成]
    H --> I[マニフェスト出力]
    I --> J[ランタイム]
    J --> K{SRIManifest存在?}
    K -->|Yes| L[integrity属性付与]
    K -->|No| M[integrity属性なし]
    L --> N[ReactDOM.preinit with integrity]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-105-1 | ハッシュアルゴリズム | sha256、sha384、sha512のいずれかを指定可能 | SRI設定が有効な場合 |
| BR-105-2 | マニフェスト形式 | JSON形式とJS形式の2つを生成（サーバーサイド用） | ビルド時 |
| BR-105-3 | integrity属性形式 | `{algorithm}-{base64hash}`のW3C SRI仕様準拠形式 | すべてのSRI値 |
| BR-105-4 | 全アセット対象 | コンパイル時に生成されたすべてのアセットに対してハッシュを計算 | ビルド時 |
| BR-105-5 | preinit統合 | ReactDOM.preinitコールにintegrity/crossOriginを含める | SRIManifest存在時 |

### 計算ロジック

SRIハッシュ計算: `crypto.createHash(algorithm).update(buffer).digest().toString('base64')`
SRI文字列: `${algorithm}-${base64hash}`

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

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | アセット取得エラー | compilation.getAsset()がundefinedを返した場合 | `could not get asset: ${file}`メッセージでErrorをスロー |

### リトライ仕様

リトライは不要。ビルドエラーとして処理される。

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

該当なし。

## パフォーマンス要件

- ビルド時にすべてのアセットのハッシュを計算するため、アセット数に比例してビルド時間が増加
- PROCESS_ASSETS_STAGE_ADDITIONSステージで実行されるため、ビルドパイプラインの後半で処理

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

- SRIはCDN経由のリソース改ざんを検出するための防御層として機能
- sha256以上のハッシュアルゴリズムはW3C SRI仕様で推奨されている
- integrity属性を使用する場合、CORS対応のためcrossOrigin属性が必要

## 備考

- `SUBRESOURCE_INTEGRITY_MANIFEST`定数は`src/shared/lib/constants.ts`で定義されている
- マニフェストのJS版はWeb Worker等のグローバルスコープ用に`self.__SUBRESOURCE_INTEGRITY_MANIFEST`に設定される

---

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

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

### 推奨読解順序

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

SRIアルゴリズムの型定義とマニフェスト形式を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | subresource-integrity-plugin.ts | `packages/next/src/build/webpack/plugins/subresource-integrity-plugin.ts` | SubresourceIntegrityAlgorithm型（7行目）: 'sha256' \| 'sha384' \| 'sha512' |
| 1-2 | constants.ts | `packages/next/src/shared/lib/constants.ts` | SUBRESOURCE_INTEGRITY_MANIFEST定数 |

**読解のコツ**: マニフェストは`Record<string, string>`（ファイル名 -> SRI値）の単純なキー/値マッピングである。

#### Step 2: ビルド時のハッシュ生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | subresource-integrity-plugin.ts | `packages/next/src/build/webpack/plugins/subresource-integrity-plugin.ts` | SubresourceIntegrityPluginクラス（9-70行目） |

**主要処理フロー**:
1. **12行目**: `apply`メソッドでWebpackコンパイラにフックを登録
2. **13行目**: `compiler.hooks.make.tap`でコンパイル時フックを設定
3. **14-17行目**: `afterProcessAssets`フック（PROCESS_ASSETS_STAGE_ADDITIONS）を登録
4. **21-24行目**: 全アセットのファイル名をSetに収集
5. **28-45行目**: 各ファイルのバッファを取得し、暗号ハッシュを計算
6. **48-65行目**: JSON形式とJS形式のマニフェストファイルを出力

#### Step 3: ランタイムでのintegrity属性適用を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | required-scripts.tsx | `packages/next/src/server/app-render/required-scripts.tsx` | SRIManifestを使用したintegrity属性の付与 |

**主要処理フロー**:
- **37行目**: SRIManifest存在チェック
- **38-39行目**: ブートストラップスクリプトにsrcとintegrity属性を設定
- **41-44行目**: 追加スクリプトのsrcとintegrityペアを配列に格納
- **46-55行目**: ReactDOM.preinitにintegrity/crossOriginを渡す

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

```
next build
    │
    ├─ webpack-config.ts [SRI設定チェック]
    │      └─ SubresourceIntegrityPlugin(algorithm)
    │             └─ apply(compiler)
    │                    └─ afterProcessAssets()
    │                           ├─ compilation.getAssets()
    │                           ├─ crypto.createHash()
    │                           └─ compilation.emitAsset()
    │
    └─ [ランタイム] app-render.tsx
           └─ required-scripts.tsx
                  └─ getRequiredScripts()
                         ├─ SRIManifest[file] → integrity
                         └─ ReactDOM.preinit({ integrity })
```

### データフロー図

```
[ビルド時]
アセットファイル群 ──> SubresourceIntegrityPlugin ──> SRIマニフェスト
                          │                              (.json/.js)
                          ├─ buffer取得
                          ├─ ハッシュ計算
                          └─ SRI文字列生成

[ランタイム]
SRIマニフェスト ──> getRequiredScripts() ──> <script integrity="...">
                       │                         ブラウザが検証
                       └─ ReactDOM.preinit()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| subresource-integrity-plugin.ts | `packages/next/src/build/webpack/plugins/subresource-integrity-plugin.ts` | ソース | Webpackプラグイン：SRIハッシュ計算とマニフェスト生成 |
| required-scripts.tsx | `packages/next/src/server/app-render/required-scripts.tsx` | ソース | ランタイムでのintegrity属性付与 |
| config-shared.ts | `packages/next/src/server/config-shared.ts` | ソース | SRI設定の型定義 |
| constants.ts | `packages/next/src/shared/lib/constants.ts` | ソース | SUBRESOURCE_INTEGRITY_MANIFEST定数 |
| webpack-config.ts | `packages/next/src/build/webpack-config.ts` | ソース | SRIプラグインのWebpack設定への組み込み |
| load-components.ts | `packages/next/src/server/load-components.ts` | ソース | SRIマニフェストの読み込み |
