# 機能設計書 34-ファイル配信

## 概要

本ドキュメントは、Laravel Frameworkにおけるファイル配信（File Serving）機能の設計書である。保護されたローカルストレージのファイルを認証済みユーザーにセキュアに配信する機能を提供し、署名付きURLによるアクセス制御を実現する。

### 本機能の処理概要

**業務上の目的・背景**：
ユーザーがアップロードしたプライベートファイル（契約書、請求書、個人情報を含むドキュメント等）は、パブリックディレクトリに配置できない。しかし、認証済みユーザーには適切にファイルをダウンロードさせる必要がある。本機能は、署名付きURLを使用してファイルへのアクセスを制御し、認可されたリクエストのみにファイルを配信する。これにより、プライベートストレージのファイルを安全に提供できる。

**機能の利用シーン**：
- ユーザーがアップロードしたプライベートドキュメントのダウンロード
- 有料コンテンツのダウンロード提供
- 期限付きファイルリンクの生成
- 認証済みユーザーへの画像配信
- 一時的なファイルアクセス許可

**主要な処理内容**：
1. 署名付きURL検証（リクエストの署名が有効かを確認）
2. ファイル存在確認（指定パスにファイルが存在するか確認）
3. ファイル配信（適切なヘッダーを設定してファイルをストリーミング）
4. セキュリティヘッダー設定（Content-Security-Policy、Cache-Control）
5. ファイルアップロード受信（署名付きURLでのPUTリクエスト対応）

**関連システム・外部連携**：
- FilesystemAdapter: ストレージアクセス
- UrlGenerator: 署名付きURL生成
- ルーティングシステム: エンドポイント登録

**権限による制御**：
- publicディスク: 署名検証なしでアクセス可能
- privateディスク: 有効な署名付きURLが必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はAPI/ファイルダウンロードのため、直接関連する画面はない |

## 機能種別

ファイル配信 / アクセス制御 / セキュリティ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | string | Yes | ファイルパス（URLパラメータ） | パストラバーサル防止 |
| signature | string | Yes（private時） | URL署名 | 有効な署名 |
| expires | int | Yes（署名付き時） | 有効期限タイムスタンプ | 未来の日時 |

### 入力データソース

- HTTPリクエスト（GET: ダウンロード、PUT: アップロード）
- 設定ファイル（`config/filesystems.php`）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| response | StreamedResponse | ファイルコンテンツのストリーム |
| headers | array | Content-Type, Content-Length等のヘッダー |

### 出力先

- HTTPレスポンス

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ GET /storage/{path} または設定されたURL

2. 署名検証
   └─ privateディスクの場合、hasValidRelativeSignature()で検証
   └─ 無効な場合は403（本番）または404（開発）

3. ファイル存在確認
   └─ Storage::disk()->exists($path)で確認
   └─ 存在しない場合は404

4. パストラバーサル防止
   └─ Flysystemが自動検出
   └─ 検出時は404

5. レスポンスヘッダー設定
   └─ Cache-Control: no-store, no-cache
   └─ Content-Security-Policy: sandbox

6. ファイル配信
   └─ Storage::disk()->serve()でストリーミング
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{アップロードフラグ?}
    B -->|Yes| C[ReceiveFile処理]
    B -->|No| D{visibility?}
    D -->|public| E[署名検証スキップ]
    D -->|private| F{署名検証}
    F -->|有効| G[ファイル存在確認]
    F -->|無効| H{本番環境?}
    H -->|Yes| I[404 Not Found]
    H -->|No| J[403 Forbidden]
    E --> G
    G -->|存在| K[セキュリティヘッダー設定]
    G -->|不存在| I
    K --> L[ファイル配信]
    L --> M[レスポンス返却]
    C --> N{署名検証}
    N -->|有効| O[ファイル保存]
    N -->|無効| H
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-34-01 | 署名必須 | privateディスクへのアクセスには有効な署名が必要 | visibility=private |
| BR-34-02 | 本番環境エラー | 本番環境では無効なリクエストに404を返す | app.env=production |
| BR-34-03 | キャッシュ無効化 | 配信ファイルはキャッシュしない | 常時 |
| BR-34-04 | CSP適用 | XSS対策としてsandbox CSPを適用 | 常時 |
| BR-34-05 | ローカルドライバ限定 | ファイル配信はローカルドライバのみ対応 | driver=local |

### 計算ロジック

該当なし

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

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | 署名無効（開発環境） | 正しい署名付きURLを使用 |
| 404 | Not Found | ファイル不存在または署名無効（本番） | パスとファイル存在を確認 |
| 404 | Not Found | パストラバーサル検出 | 正しいパスを使用 |

### リトライ仕様

本機能にリトライ機能は含まれない。

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

該当なし

## パフォーマンス要件

- ファイルはストリーミング配信され、メモリに全体を読み込まない
- 大きなファイルも効率的に配信可能
- Cache-Control: no-storeによりブラウザキャッシュを防止

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

- **署名検証**: 署名なしまたは無効な署名でのアクセスを拒否
- **パストラバーサル防止**: `../`等を使用したディレクトリ外アクセスを検出・拒否
- **CSPヘッダー**: `default-src 'none'; style-src 'unsafe-inline'; sandbox`で実行可能コンテンツを制限
- **キャッシュ制御**: 機密ファイルがキャッシュされないようno-store設定
- **本番環境保護**: エラー時に404を返し、ファイル存在を推測されにくくする

## 備考

- `serve`オプションを`true`に設定したローカルディスクでのみ有効
- ルートキャッシュが有効な場合、serveFilesは実行されない
- カスタムURLパスは`config/filesystems.php`の`url`オプションで設定可能

---

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

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

### 推奨読解順序

#### Step 1: ルート登録を理解する

まず、ファイル配信ルートがどのように登録されるかを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | FilesystemServiceProvider.php | `src/Illuminate/Filesystem/FilesystemServiceProvider.php` | serveFilesメソッドでのルート登録 |

**主要処理フロー**:
- **80-115行目**: `serveFiles()` - ファイル配信ルートの動的登録
- **86-89行目**: `serve`オプションが有効なディスクを検出
- **98-104行目**: GETルート登録（ServeFile）
- **106-112行目**: PUTルート登録（ReceiveFile）
- **123-126行目**: `shouldServeFiles()` - localドライバかつserve=true判定

**読解のコツ**: ルートはアプリケーション起動時（boot）に動的に登録される。ルートキャッシュ有効時はスキップされるため、開発時に注意が必要。

#### Step 2: ServeFileクラスを理解する

ファイル配信の実処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ServeFile.php | `src/Illuminate/Filesystem/ServeFile.php` | ファイル配信のInvokableコントローラ |

**主要処理フロー**:
- **14-18行目**: コンストラクタ - ディスク名、設定、本番フラグを保持
- **25-50行目**: `__invoke()` - メインの配信処理
- **27-29行目**: 署名検証、無効時にabort
- **32行目**: ファイル存在確認
- **34-37行目**: セキュリティヘッダー設定
- **40行目**: `Storage::disk()->serve()`呼び出し
- **47-48行目**: PathTraversalDetected例外のキャッチ
- **55-60行目**: `hasValidSignature()` - 署名検証ロジック

**読解のコツ**: ServeFileはInvokableコントローラパターンを使用。`__invoke`メソッドがエントリーポイント。

#### Step 3: FilesystemAdapterのserveメソッドを理解する

実際のファイル配信処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | FilesystemAdapter.php | `src/Illuminate/Filesystem/FilesystemAdapter.php` | serveメソッドとresponseメソッド |

**主要処理フロー**:
- **364-369行目**: `serve()` - カスタムコールバックまたはresponse呼び出し
- **327-353行目**: `response()` - StreamedResponse生成
- **331-332行目**: MIMEタイプとサイズの取得
- **346-350行目**: readStreamでストリーミング

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

```
GET /storage/{path}
    │
    └─ Route::get() で登録されたルート
           │
           └─ ServeFile::__invoke($request, $path)
                  │
                  ├─ hasValidSignature() ... 署名検証
                  │      └─ $request->hasValidRelativeSignature()
                  │
                  ├─ abort_unless() ... 無効時は中断
                  │
                  ├─ Storage::disk()->exists($path) ... 存在確認
                  │
                  └─ Storage::disk()->serve($request, $path)
                         │
                         └─ FilesystemAdapter::serve()
                                │
                                ├─ serveCallback設定あり
                                │      └─ カスタムコールバック実行
                                │
                                └─ response($path, $name, $headers)
                                       │
                                       ├─ StreamedResponse作成
                                       ├─ Content-Type設定
                                       ├─ Content-Length設定
                                       └─ readStream()でコールバック設定
```

### データフロー図

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

GET /storage/path ────────▶ ServeFile
     ├─ signature                │
     └─ expires            署名検証
                               │
                         ┌─────┴─────┐
                         │           │
                       有効        無効
                         │           │
                   ファイル確認   abort(403/404)
                         │
                    serve()呼び出し
                         │
                    StreamedResponse
                         │
                    ファイルストリーム ─────────▶ HTTPレスポンス
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ServeFile.php | `src/Illuminate/Filesystem/ServeFile.php` | ソース | ファイル配信コントローラ |
| ReceiveFile.php | `src/Illuminate/Filesystem/ReceiveFile.php` | ソース | ファイルアップロード受信コントローラ |
| FilesystemServiceProvider.php | `src/Illuminate/Filesystem/FilesystemServiceProvider.php` | ソース | ルート登録 |
| FilesystemAdapter.php | `src/Illuminate/Filesystem/FilesystemAdapter.php` | ソース | serve/responseメソッド |
| LocalFilesystemAdapter.php | `src/Illuminate/Filesystem/LocalFilesystemAdapter.php` | ソース | 署名付きURL生成 |
| UrlGenerator.php | `src/Illuminate/Routing/UrlGenerator.php` | ソース | 署名付きURL生成 |
