# 機能設計書 19-Strong Parameters

## 概要

本ドキュメントは、Ruby on RailsフレームワークにおけるStrong Parameters機能の設計を記載する。Strong Parametersは、マスアサインメント脆弱性を防ぐため、許可されたパラメータのみをモデルに渡すホワイトリスト方式のフィルタリング機能を提供する。

### 本機能の処理概要

Strong Parametersは、HTTPリクエストのパラメータをActionController::Parametersオブジェクトとしてラップし、明示的に許可されたパラメータのみをActive Recordモデルに渡すことを可能にする。

**業務上の目的・背景**：
マスアサインメント脆弱性は、ユーザーが意図しないパラメータ（管理者フラグ、価格等）を送信し、モデルに設定されてしまう攻撃である。Rails 3以前では`attr_accessible`でモデル側で保護していたが、Rails 4以降はStrong Parametersでコントローラ側で明示的にパラメータを許可する設計に変更された。これにより、アクションごとに異なる許可ルールを柔軟に適用できる。

**機能の利用シーン**：
- フォームからのユーザー入力の受け取り
- APIリクエストのパラメータ処理
- ネストしたパラメータの許可
- 配列パラメータの許可

**主要な処理内容**：
1. `params`オブジェクトのParametersラッピング
2. `require`による必須パラメータの要求
3. `permit`による許可パラメータの宣言
4. `expect`による安全な必須+許可の統合
5. 未許可パラメータの検出とログ/例外

**関連システム・外部連携**：
- Action Controller（パラメータ取得）
- Active Record（マスアサインメント）
- Active Model（ForbiddenAttributesError）

**権限による制御**：
- Strong Parameters自体がパラメータのホワイトリスト機能
- アクションごとに許可パラメータを変更可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 全画面共通 | 横断 | フォーム送信時のパラメータフィルタリング |

## 機能種別

セキュリティ / パラメータ制御 / マスアサインメント保護

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| request.parameters | Hash | Yes | HTTPリクエストパラメータ | - |

### 入力データソース

- HTTPリクエストパラメータ
- ルーティングパラメータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Parameters | ActionController::Parameters | 許可済みパラメータオブジェクト |
| to_h | Hash | 許可済みパラメータのHash変換 |
| to_unsafe_h | Hash | 未フィルタリングのHash（非推奨） |

### 出力先

- Active Recordモデルへのマスアサインメント

## 処理フロー

### 処理シーケンス

```
1. パラメータ取得
   └─ params = ActionController::Parameters.new(request.parameters)

2. 必須パラメータの要求
   └─ params.require(:user)
      └─ 存在しない場合 → ParameterMissing例外

3. 許可パラメータの宣言
   └─ params.permit(:name, :email)
      └─ 未許可パラメータ → フィルタリング/ログ/例外

4. 統合アプローチ（推奨）
   └─ params.expect(user: [:name, :email])
      └─ require + permit を安全に実行

5. モデルへの適用
   └─ User.create(permitted_params)
```

### フローチャート

```mermaid
flowchart TD
    A[HTTPリクエスト] --> B[request.parameters]
    B --> C[ActionController::Parameters.new]
    C --> D{require?}
    D -->|Yes| E[params.require key ]
    E --> F{キー存在?}
    F -->|No| G[ParameterMissing]
    F -->|Yes| H[値取得]
    D -->|No| H
    H --> I{permit?}
    I -->|Yes| J[params.permit keys ]
    J --> K[許可パラメータのみ抽出]
    K --> L{未許可パラメータ?}
    L -->|Yes action: :log| M[ログ出力]
    L -->|Yes action: :raise| N[UnpermittedParameters]
    L -->|No| O[permitted: true]
    I -->|No| P[permitted: false]
    O --> Q[to_h可能]
    P --> R[to_h → UnfilteredParameters]
    Q --> S[Model.create/update]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | スカラー型許可 | String, Symbol, Numeric, Boolean, Date, Time, IO等のみ許可 | permit時 |
| BR-002 | ネスト許可 | ハッシュ/配列のネストは明示的に宣言 | permit時 |
| BR-003 | 未許可警告 | 未許可パラメータはログ出力（開発/テスト） | action_on_unpermitted_parameters: :log |
| BR-004 | 未許可例外 | 未許可パラメータで例外発生 | action_on_unpermitted_parameters: :raise |
| BR-005 | controller/action除外 | controller/actionパラメータは常に許可扱い | 常時 |

### 計算ロジック

**許可スカラー型**（PERMITTED_SCALAR_TYPES）：
- String, Symbol, NilClass, Numeric
- TrueClass, FalseClass
- Date, Time, DateTime
- StringIO, IO
- ActionDispatch::Http::UploadedFile
- Rack::Test::UploadedFile

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

Strong Parametersはデータベースを直接操作しない。

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ParameterMissing | 例外 | require/expectで必須パラメータなし | パラメータを確認 |
| ExpectedParameterMissing | 例外 | expect!で必須パラメータなし | パラメータを確認（500エラー） |
| UnpermittedParameters | 例外 | action_on_unpermitted: :raise時 | permitを確認 |
| UnfilteredParameters | 例外 | 未許可パラメータのto_h | permitを追加 |
| InvalidParameterKey | 例外 | キーがString/Symbol以外 | キーの型を確認 |

### リトライ仕様

特になし

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

特になし

## パフォーマンス要件

- パラメータフィルタリングはリクエストごとに実行
- フィルタリング処理のオーバーヘッドは最小限

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

- マスアサインメント脆弱性の防止
- 明示的なホワイトリスト方式
- ネストしたパラメータの厳格な制御
- 型チェックによる不正データの排除

## 備考

- Rails 4.0で導入（`attr_accessible`を置き換え）
- `expect`メソッドはRails 7.1で追加（`require.permit`より安全）
- `permit_all_parameters`で全許可（非推奨、テスト用途のみ）
- ネストした属性（`accepts_nested_attributes_for`）も対応

---

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

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

### 推奨読解順序

#### Step 1: 例外クラスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | strong_parameters.rb | `actionpack/lib/action_controller/metal/strong_parameters.rb` | 例外クラス定義 |

**読解のコツ**: 各例外クラスの用途と発生条件を理解する。

**主要処理フロー**（strong_parameters.rb）:
- **25-41行目**: `ParameterMissing`例外
- **49-50行目**: `ExpectedParameterMissing`例外
- **59-66行目**: `UnpermittedParameters`例外
- **74-78行目**: `UnfilteredParameters`例外
- **84-85行目**: `InvalidParameterKey`例外

#### Step 2: Parametersクラスの基本を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | strong_parameters.rb | `actionpack/lib/action_controller/metal/strong_parameters.rb` | Parametersクラス初期化 |

**主要処理フロー**:
- **163行目**: `permit_all_parameters`設定
- **165行目**: `action_on_unpermitted_parameters`設定
- **263行目**: `always_permitted_parameters`（controller, action）
- **287-297行目**: `initialize`
- **449-451行目**: `permitted?`

#### Step 3: require/permitを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | strong_parameters.rb | `actionpack/lib/action_controller/metal/strong_parameters.rb` | require/permitメソッド |

**主要処理フロー**:
- **523-531行目**: `require`メソッド
- **672-674行目**: `permit`メソッド
- **1302-1320行目**: PERMITTED_SCALAR_TYPES定義
- **1318-1320行目**: `permitted_scalar?`

#### Step 4: expectを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | strong_parameters.rb | `actionpack/lib/action_controller/metal/strong_parameters.rb` | expectメソッド |

**主要処理フロー**:
- **776-781行目**: `expect`メソッド
- **790-794行目**: `expect!`メソッド

#### Step 5: フィルタリング処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | strong_parameters.rb | `actionpack/lib/action_controller/metal/strong_parameters.rb` | 内部フィルタリング |

**主要処理フロー**:
- **1134-1151行目**: `permit_filters`
- **1276-1288行目**: `unpermitted_parameters!`
- **1332-1345行目**: `permitted_scalar_filter`
- **1353-1363行目**: `hash_filter`
- **1365-1377行目**: `permit_value`

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

```
コントローラアクション
    │
    └─ params
           │
           └─ ActionController::Parameters.new(request.parameters)
                  │
                  └─ @parameters = parameters.with_indifferent_access
                         │
                         └─ @permitted = false


params.expect(user: [:name, :email])
    │
    └─ permit_filters(filters)
           │
           ├─ hash_filter(params, filter)
           │      │
           │      └─ permit_value(value, filter)
           │             │
           │             ├─ EMPTY_ARRAY → permit_array_of_scalars
           │             │
           │             ├─ EMPTY_HASH → permit_any_in_parameters
           │             │
           │             └─ その他 → permit_hash / permit_array_of_hashes
           │
           ├─ unpermitted_parameters!(params)
           │      │
           │      ├─ :log → ActiveSupport::Notifications.instrument
           │      │
           │      └─ :raise → UnpermittedParameters
           │
           └─ params.permit!
                  │
                  └─ @permitted = true


params.require(:user)
    │
    ├─ value = self[:user]
    │
    └─ value.present? || value == false
           │
           ├─ true → value返却
           │
           └─ false → raise ParameterMissing
```

### データフロー図

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

HTTPリクエスト ───────────▶ request.parameters
                                │
                                ▼
                    ActionController::Parameters.new
                                │
                                ├─▶ @parameters（Hash）
                                │
                                └─▶ @permitted = false
                                          │
                                          ▼
                    ┌─────────────────────┴─────────────────────┐
                    │                                           │
                    ▼                                           ▼
            require(:key)                              permit(:keys)
                    │                                           │
                    ├─▶ 存在 → 値返却                          ├─▶ フィルタリング
                    │                                           │
                    └─▶ 不在 → ParameterMissing                └─▶ @permitted = true
                                                                    │
                                                                    ▼
                                                            permitted_params
                                                                    │
                                                                    ▼
                                                            to_h → Hash
                                                                    │
                                                                    ▼
                                                            Model.create
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| strong_parameters.rb | `actionpack/lib/action_controller/metal/strong_parameters.rb` | ソース | Parameters/StrongParametersモジュール |
| forbidden_attributes_protection.rb | `activemodel/lib/active_model/forbidden_attributes_protection.rb` | ソース | ForbiddenAttributesError |
