# 機能設計書 18-Content Security Policy

## 概要

本ドキュメントは、Ruby on RailsフレームワークにおけるContent Security Policy（CSP）機能の設計を記載する。CSPは、XSS（クロスサイトスクリプティング）攻撃やデータインジェクション攻撃を防ぐためのHTTPレスポンスヘッダーを生成する。

### 本機能の処理概要

Content Security Policyは、ブラウザに対してどのリソース（スクリプト、スタイル、画像等）をロードしてよいかを指示するセキュリティ機構である。Railsは、DSLを通じてCSPヘッダーを宣言的に設定できる。

**業務上の目的・背景**：
XSS攻撃は、悪意のあるスクリプトをWebページに注入し、ユーザーのセッション情報を盗んだり、不正な操作を行ったりする。CSPを適切に設定することで、信頼されたソースからのスクリプトのみを実行許可し、インラインスクリプトや`eval()`の使用を制限できる。これにより、XSS攻撃の影響を大幅に軽減できる。

**機能の利用シーン**：
- スクリプトソースの制限
- スタイルソースの制限
- 画像・フォント・メディアソースの制限
- フレーム埋め込みの制限
- 違反レポートの収集
- nonce付きインラインスクリプトの許可

**主要な処理内容**：
1. CSPポリシーの定義（`content_security_policy`ブロック）
2. ディレクティブの設定（script-src, style-src等）
3. nonceの生成とインラインスクリプトへの適用
4. CSPヘッダーの生成とレスポンスへの付加
5. レポートモード（Report-Only）の設定

**関連システム・外部連携**：
- Action Dispatch（ミドルウェア）
- Action View（nonce付きスクリプトタグ）
- ブラウザ（CSP実行）
- 違反レポート収集サーバー

**権限による制御**：
- CSP自体がリソースロードの制御機能
- コントローラごとにポリシーを上書き可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 全画面共通 | 横断 | CSPヘッダーの付加 |

## 機能種別

セキュリティ / HTTPヘッダー / XSS対策

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| policy | ContentSecurityPolicy | No | CSPポリシーオブジェクト | - |
| nonce | String | No | スクリプト/スタイル用nonce | - |
| nonce_directives | Array | No | nonceを適用するディレクティブ | - |

### 入力データソース

- `config/initializers/content_security_policy.rb`
- コントローラでの`content_security_policy`設定

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Content-Security-Policy | String | CSPヘッダー値 |
| Content-Security-Policy-Report-Only | String | レポートモード用ヘッダー |

### 出力先

- HTTPレスポンスヘッダー

## 処理フロー

### 処理シーケンス

```
1. CSPポリシーの設定
   └─ Rails.application.config.content_security_policy do |policy|
      └─ policy.default_src :self
      └─ policy.script_src :self, :https
      └─ ...

2. リクエスト処理時
   └─ ContentSecurityPolicy::Middleware#call
      └─ request.content_security_policy 取得
      └─ nonce生成
      └─ policy.build(context, nonce, nonce_directives)

3. ヘッダー生成
   └─ ディレクティブ文字列の組み立て
   └─ nonce付加
   └─ レスポンスヘッダーに設定
```

### フローチャート

```mermaid
flowchart TD
    A[config/initializers/csp.rb] --> B[グローバルポリシー定義]
    B --> C[ContentSecurityPolicy::Middleware]
    D[HTTPリクエスト] --> C
    C --> E{304 Not Modified?}
    E -->|Yes| F[ヘッダーなしで返却]
    E -->|No| G{既存CSPヘッダー?}
    G -->|Yes| F
    G -->|No| H[request.content_security_policy取得]
    H --> I{policy存在?}
    I -->|No| J[ヘッダーなし]
    I -->|Yes| K[nonce生成]
    K --> L[policy.build]
    L --> M[ディレクティブ組み立て]
    M --> N[nonce付加]
    N --> O{report_only?}
    O -->|Yes| P[Content-Security-Policy-Report-Only]
    O -->|No| Q[Content-Security-Policy]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 304除外 | 304レスポンスにはCSPヘッダーを付加しない | 常時 |
| BR-002 | 既存ヘッダー優先 | 既にCSPヘッダーがある場合は上書きしない | 常時 |
| BR-003 | nonce適用 | script-src/style-srcにnonceを自動付加 | nonce_generator設定時 |
| BR-004 | シンボルマッピング | :self → 'self'、:unsafe_inline → 'unsafe-inline' | 常時 |

### 計算ロジック

**ディレクティブ値のマッピング**：
- `:self` → `'self'`
- `:unsafe_eval` → `'unsafe-eval'`
- `:unsafe_inline` → `'unsafe-inline'`
- `:none` → `'none'`
- `:https` → `https:`
- `:strict_dynamic` → `'strict-dynamic'`

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

Content Security Policyはデータベースを操作しない。

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| InvalidDirectiveError | 例外 | ディレクティブ値に空白や;を含む | 複数値は配列で指定 |
| ArgumentError | 例外 | 不明なソースマッピング | 正しいシンボルを使用 |
| RuntimeError | 例外 | 動的ソースでコンテキストなし | コントローラコンテキストを確認 |

### リトライ仕様

特になし

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

特になし

## パフォーマンス要件

- CSPヘッダー生成はリクエストごとに実行
- nonce生成のオーバーヘッドは最小限

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

- XSS攻撃の軽減
- インラインスクリプトの制限
- eval()の無効化
- 信頼されたソースのみ許可
- 違反レポートによる監視

## 備考

- CSPはW3C標準のセキュリティ仕様
- `rails new`でデフォルトのCSP設定ファイルが生成される
- nonceはリクエストごとにランダム生成
- Report-Onlyモードで本番導入前にテスト可能

---

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

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

### 推奨読解順序

#### Step 1: CSPポリシークラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | content_security_policy.rb | `actionpack/lib/action_dispatch/http/content_security_policy.rb` | ContentSecurityPolicyクラス全体 |

**読解のコツ**: MAPPINGSとDIRECTIVESの定義を確認し、各ディレクティブメソッドの動的定義を理解する。

**主要処理フロー**（content_security_policy.rb）:
- **28-31行目**: InvalidDirectiveError定義
- **128-147行目**: MAPPINGS（シンボル→文字列マッピング）
- **149-172行目**: DIRECTIVES（ディレクティブ名マッピング）
- **176行目**: DEFAULT_NONCE_DIRECTIVES
- **182-185行目**: `initialize`
- **191-199行目**: 各ディレクティブメソッドの動的定義
- **299-302行目**: `build`メソッド（ヘッダー文字列生成）

#### Step 2: ミドルウェアを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | content_security_policy.rb | `actionpack/lib/action_dispatch/http/content_security_policy.rb` | Middlewareクラス |

**主要処理フロー**:
- **32-71行目**: `Middleware`クラス
- **37-56行目**: `call`メソッド
- **42行目**: 304レスポンスのスキップ
- **44行目**: 既存ヘッダーのチェック
- **48-52行目**: ポリシー取得とヘッダー設定
- **59-65行目**: `header_name`（report_only判定）

#### Step 3: Requestモジュールを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | content_security_policy.rb | `actionpack/lib/action_dispatch/http/content_security_policy.rb` | Requestモジュール |

**主要処理フロー**:
- **73-126行目**: `Request`モジュール
- **74-78行目**: 環境変数キー定義
- **80-94行目**: ポリシーアクセサ
- **96-110行目**: nonceディレクティブアクセサ
- **112-120行目**: nonce生成

#### Step 4: 特殊ディレクティブを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | content_security_policy.rb | `actionpack/lib/action_dispatch/http/content_security_policy.rb` | 特殊ディレクティブメソッド |

**主要処理フロー**:
- **210-216行目**: `block_all_mixed_content`
- **226-232行目**: `plugin_types`
- **240-242行目**: `report_uri`
- **252-258行目**: `require_sri_for`
- **273-281行目**: `sandbox`
- **291-297行目**: `upgrade_insecure_requests`

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

```
config/initializers/content_security_policy.rb
    │
    └─ Rails.application.config.content_security_policy do |policy|
           │
           └─ ContentSecurityPolicy.new
                  │
                  ├─ policy.default_src :self
                  │      └─ @directives["default-src"] = ["'self'"]
                  │
                  └─ policy.script_src :self, :https
                         └─ @directives["script-src"] = ["'self'", "https:"]


HTTPリクエスト
    │
    └─ ContentSecurityPolicy::Middleware#call(env)
           │
           ├─ response = @app.call(env)
           │
           ├─ return if status == 304
           │
           ├─ return if policy_present?(headers)
           │
           ├─ request.content_security_policy
           │      │
           │      └─ get_header(POLICY)
           │
           ├─ request.content_security_policy_nonce
           │      │
           │      └─ generate_content_security_policy_nonce
           │
           └─ policy.build(context, nonce, nonce_directives)
                  │
                  ├─ build_directives
                  │      │
                  │      ├─ apply_mappings
                  │      │
                  │      └─ nonce付加
                  │
                  └─ join("; ")
```

### データフロー図

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

initializers/csp.rb ────▶ ContentSecurityPolicy設定
                                │
                                ▼
                          グローバルポリシー保存
                                │
                                ▼
HTTPリクエスト ──────────▶ Middleware#call
                                │
                                ├─▶ ポリシー取得
                                │
                                ├─▶ nonce生成
                                │
                                └─▶ policy.build
                                       │
                                       ▼
                                ディレクティブ文字列
                                       │
                                       ▼
                          ┌────────────┴────────────┐
                          │                         │
                          ▼                         ▼
              Content-Security-Policy    Content-Security-Policy-Report-Only
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| content_security_policy.rb | `actionpack/lib/action_dispatch/http/content_security_policy.rb` | ソース | CSPクラス・ミドルウェア |
| javascript_helper.rb | `actionview/lib/action_view/helpers/javascript_helper.rb` | ソース | nonce付きスクリプトタグ |
| csp_helper.rb | `actionview/lib/action_view/helpers/csp_helper.rb` | ソース | CSPヘルパー |
| content_security_policy.rb | `railties/lib/rails/generators/rails/app/templates/config/initializers/content_security_policy.rb.tt` | テンプレート | デフォルト設定 |
