# セキュリティ設計書

## 概要

本ドキュメントは、Liam HQプロジェクトのセキュリティ設計について記載する。本プロジェクトはNext.js 15をベースとしたWebアプリケーションであり、Supabaseをバックエンドとして使用している。認証にはSupabase AuthとGitHub OAuthを採用し、データベースアクセスにはRow Level Security (RLS)を適用している。

## 認証設計

### 認証方式

本システムはSupabase Authを使用した認証基盤を採用しており、以下の認証方式をサポートしている。

| 認証方式 | 説明 | 実装ファイル |
| --- | --- | --- |
| GitHub OAuth | GitHubアカウントを使用したソーシャルログイン | `frontend/apps/app/components/LoginPage/services/loginByGithub.ts` |
| Email認証 | メールアドレスによる認証（Supabase Auth経由） | `frontend/apps/app/components/LoginPage/services/loginByEmail.ts` |

### セッション管理

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| セッション管理 | Supabase Auth Cookie | Supabaseのサーバークライアントを通じて管理 |
| セッション有効期限 | Supabase管理 | Supabase Authのデフォルト設定に準拠 |
| セッション固定化対策 | `auth.exchangeCodeForSession()` | OAuthコールバック時にコードをセッションに交換 |
| returnTo Cookie | 1時間（3600秒） | ログイン後のリダイレクト先を一時保存 |

### GitHub OAuthトークン管理

GitHubのOAuthトークンは、セキュアなHttpOnly Cookieに暗号化して保存される。

| 項目 | 設定値 | 備考 |
| --- | --- | --- |
| アクセストークン有効期限 | 8時間 | GitHub OAuthの仕様に準拠 |
| リフレッシュトークン有効期限 | 6ヶ月 | GitHub OAuthの仕様に準拠 |
| トークン保存方式 | AES-256-GCM暗号化 + HttpOnly Cookie | `frontend/apps/app/libs/github/cookie.ts` |
| Cookie名 | `liam_at`（アクセス）, `liam_rt`（リフレッシュ） | 暗号化されたペイロードを格納 |

## 認可設計

### 権限体系

| ロール | 権限 | 説明 |
| --- | --- | --- |
| authenticated | 組織メンバーとしてのCRUD | 認証済みユーザー、所属組織のリソースにアクセス可能 |
| service_role | 全リソースへのフルアクセス | サーバーサイドでRLSをバイパスする場合に使用 |
| anon | アクセス不可 | 全ての権限を剥奪済み |

### アクセス制御

本システムは、PostgreSQLのRow Level Security (RLS)をベースとしたアクセス制御を実装している。

**主要なRLSポリシー:**

1. **usersテーブル**: 同一組織に所属するユーザーのみ相互参照可能
2. **organizationsテーブル**: メンバーのみが更新・削除可能
3. **organization_membersテーブル**: 所属組織のメンバー情報のみ参照・操作可能
4. **invitationsテーブル**: 組織メンバーのみが招待状を管理可能
5. **github_repositoriesテーブル**: 組織メンバーのみがリポジトリ情報にアクセス可能

**匿名ユーザーの権限剥奪:**

`20250813124330_revoke_anon_permissions.sql`により、`anon`ロールの全権限を明示的に剥奪している。

## 通信セキュリティ

| 項目 | 対策 |
| --- | --- |
| HTTPS | 本番環境ではHTTPS必須（Vercel/Supabaseインフラによる強制） |
| Cookie Secure属性 | 本番環境では`secure: true`を設定 |
| SameSite属性 | `lax`を設定してCSRF対策を実施 |
| X-Frame-Options | `DENY`（ERDパス直接アクセス拒否時のレスポンスヘッダー） |
| X-Content-Type-Options | `nosniff`（ERDパス直接アクセス拒否時のレスポンスヘッダー） |
| Cache-Control | セキュリティ関連レスポンスでは`no-store, no-cache, must-revalidate, private` |

## データセキュリティ

### 暗号化

| 対象 | 暗号化方式 |
| --- | --- |
| 通信 | TLS（Vercel/Supabaseインフラによる提供） |
| GitHub OAuthトークン | AES-256-GCM（12バイトIV、16バイト認証タグ） |
| データベース | Supabaseマネージドによる暗号化 |

**AES-256-GCM実装詳細:**

- 実装ファイル: `frontend/internal-packages/security/src/cryptoBox.ts`
- キーリング管理: 環境変数`LIAM_GITHUB_OAUTH_KEYRING`で複数キーをローテーション可能
- キー形式: `<kid>:<base64key>`のカンマ区切り（先頭が現在のキー）
- キー長: 32バイト（256ビット）

### 機密情報管理

| 機密情報 | 管理方式 |
| --- | --- |
| 環境変数 | `.env.template`で定義、実値はVercel/ホスティング環境で管理 |
| Supabaseキー | `SUPABASE_SERVICE_ROLE_KEY`（サーバーサイドのみ使用） |
| GitHubシークレット | `GITHUB_CLIENT_SECRET`, `GITHUB_PRIVATE_KEY` |
| 暗号化キーリング | `LIAM_GITHUB_OAUTH_KEYRING` |
| APIキー | `OPENAI_API_KEY`, `LANGSMITH_API_KEY`, `SENTRY_AUTH_TOKEN`, `RESEND_API_KEY` |

## 入出力対策

| 脅威 | 対策 |
| --- | --- |
| XSS | React/Next.jsのデフォルトエスケープ、SQL識別子のエスケープ処理 |
| SQLインジェクション | Supabaseクライアント使用によるパラメータ化クエリ、SQL識別子エスケープ（`escapeIdentifier`関数） |
| CSRF | SameSite Cookie属性（`lax`）、Server Actions の`allowedOrigins`設定 |
| オープンリダイレクト | `sanitizeReturnPath`による相対パス検証 |
| ReDoS | 型安全な配列処理（`while`ループによる`[]`検出） |

**入力検証:**

- Valibotライブラリを使用したスキーマベースのバリデーション
- 実装例: `frontend/apps/app/features/sessions/components/shared/validation/sessionFormValidation.ts`
- 相対パス検証: `frontend/apps/app/components/LoginPage/services/validateReturnPath.ts`

**Server Actions許可オリジン:**

本番環境では`liambx.com`と`app.liambx.com`のみをServer Actionsの許可オリジンとして設定している。

## 監査ログ

| ログ種別 | 記録内容 | 保持期間 |
| --- | --- | --- |
| エラーログ | Sentryによる例外キャプチャ | Sentry設定に依存 |
| 認証エラー | OAuthコールバック失敗、認証失敗イベント | コンソール出力/Sentry |
| セキュリティイベント | ERD直接アクセス拒否、ログアウト処理エラー | コンソール出力/Sentry |

**Sentry統合:**

- 本番環境でのソースマップアップロード
- React コンポーネント自動アノテーション
- Vercel Cron Monitors自動インストルメンテーション

**ログ記録例:**

```typescript
// 認証Cookie書き込みエラー
Sentry.captureException(err, {
  tags: { area: 'auth_cookie_write' },
})

// ログアウト処理エラー
Sentry.captureException(error, {
  tags: { area: 'logout_api', step: 'clear_tokens' },
})
```

## 備考

### 本番環境固有の保護機能

1. **ERDパス直接アクセス制限**: 本番環境では`/erd/*`パスへの直接アクセスを禁止し、`x-liam-rewrite-source`ヘッダーが`liambx.com`の場合のみアクセスを許可している。

2. **アセットプレフィックス必須**: 本番環境では`ASSET_PREFIX`環境変数が必須であり、未設定の場合はビルドが失敗する。

### セキュリティ関連マイグレーション

| マイグレーション | 目的 |
| --- | --- |
| `20250423040522_users_table_policy.sql` | usersテーブルRLS有効化 |
| `20250425123516_add_rls_to_organizations_table.sql` | organizationsテーブルRLS有効化 |
| `20250425123428_add_rls_to_organization_members.sql` | organization_membersテーブルRLS有効化 |
| `20250425123413_add_rls_to_invitations.sql` | invitationsテーブルRLS有効化 |
| `20250425123357_add_rls_to_github_repositories.sql` | github_repositoriesテーブルRLS有効化 |
| `20250813124330_revoke_anon_permissions.sql` | 匿名ユーザー権限剥奪 |
| `20250428034646_prevent_delete_last_organization_member.sql` | 最後の組織メンバー削除防止 |
