テーマ
セキュリティ
本システムは保険業務の特性上、個人情報 および 要配慮個人情報(健康情報・マイナンバー等)を扱う前提で設計する。技術的安全管理措置を 多層防御 で実装する。
守る対象(資産)
| 区分 | 例 | 機密度 |
|---|---|---|
| 認証情報 | パスワードハッシュ、セッション、TOTPシード | 最大 |
| 外部API資格情報 | LINE Channel Token、Google Refresh Token、Stripe/Square API Key | 最大 |
| 要配慮個人情報 | 健康情報、マイナンバー、身分証番号 | 最大 |
| 個人情報 | 氏名、住所、電話、メール、生年月日 | 高 |
| 業務情報 | 契約内容、保険金額、商品 | 高 |
| ファイル | 証券スキャン、申込書、本人確認書類 | 区分による |
| 業務ログ | アクティビティ、スケジュール | 中 |
| メタ情報 | テナント設定、ユーザー設定 | 中 |
脅威モデル(簡易 STRIDE)
| 脅威 | 主な対策 |
|---|---|
| Spoofing なりすまし | 強パスワード + TOTP MFA、セッション固定対策、CSRF |
| Tampering 改ざん | TLS / HSTS、Webhook 署名検証、入力検証、監査ログ |
| Repudiation 否認 | 監査ログ(break-glass にも対応)、操作の actor / IP 記録 |
| Information Disclosure 情報漏えい | エンベロープ暗号、テナント分離、PIIマスキング、最小権限 |
| Denial of Service | レート制限、Cloudflare WAF、Workers のスケール |
| Elevation of Privilege 権限昇格 | 二重認可(MW + サービス層)、ADMIN分離、break-glass 監査 |
暗号化アーキテクチャ
全体像(エンベロープ暗号化)
- KEK (Key Encryption Key): 全テナント共通のマスタ鍵。Cloudflare Workers Secrets で保管。
- DEK (Data Encryption Key): テナント単位の対称鍵。
tenants.dek_encに KEK で暗号化して保存。 - 暗号化カラム / 高機密ファイルは テナントの DEK で暗号化。
- リクエスト処理時に DEK を復号してメモリに展開、レスポンス後に破棄。
アルゴリズム
- 対称暗号: AES-256-GCM(WebCrypto API)
- ハッシュ: SHA-256
- パスワード: Better Auth のデフォルト(Argon2id 推奨)
- 乱数:
crypto.getRandomValues
鍵ローテーション
| 鍵 | 周期 | 手順 |
|---|---|---|
| KEK | 年次 + 漏えい疑義時 | 新KEK生成 → 全テナントの dek_enc を再暗号化 → 旧KEK失効 |
| DEK | テナント要求時 / 漏えい疑義時 | 新DEK生成 → 既存暗号化カラムを再暗号化 → 旧DEK失効 |
| TOTPシード | ユーザー操作時のみ | リセット時に再生成 |
| API キー(外部連携) | 推奨6か月 | 各プロバイダの手順に従う |
暗号化対象一覧(コード命名規則: *_enc)
| 場所 | カラム | 内容 | 鍵 |
|---|---|---|---|
tenants | dek_enc | テナント DEK 自身 | KEK |
line_channels | channel_secret_enc, access_token_enc | LINE 資格情報 | テナントDEK |
external_calendar_links | refresh_token_enc | Google OAuth | テナントDEK |
payment_provider_settings (ADMIN管理) | api_key_enc, webhook_secret_enc | Stripe/Square | KEK 直下(システム層) |
customers | sensitive_data_enc(要配慮 JSON) | 健康情報、マイナンバー等 | テナントDEK |
customer_identifiers | value_enc | 身分証番号、銀行口座末尾 | テナントDEK |
webhook_events | payload_enc(任意) | 機微なペイロード | テナントDEK |
ファイル暗号化(R2)
機密区分
| 区分 | 既定保管 | プレビュー | サムネイル | 署名URL有効期限 |
|---|---|---|---|---|
public | R2 SSE | ✅ | ✅ | 60分 |
internal | R2 SSE | ✅ | ✅ | 30分 |
confidential | R2 SSE | ✅ | ✅ | 5分 |
sensitive | R2 SSE + アプリ層 AES-GCM (テナントDEK) | ❌(原寸のみDL) | ❌ | 2分 |
高機密ファイルのアップロード / ダウンロード
- 高機密ファイルは 直アップロードしない(暗号化のため Worker を経由)
- ダウンロードもプロキシ経由。署名URL直接配布は使わない
- LINE 受信添付ファイルは「
internal既定」で取り込み、ユーザーが昇格できる
認証
パスワードポリシー
- 最低12文字、英大小+数字+記号のうち3種以上
- 既知漏洩パスワード辞書(HIBP API)と照合(任意)
- 履歴: 直近5世代と同一不可(後続)
TOTP (MFA)
- OWNER / ADMIN は MFA 必須
- MEMBER は任意(テナント設定で必須化可能)
- リカバリーコード(10個)を発行、ハッシュ保存
- 端末紛失時のリセットは ADMIN 申請+本人確認
セッション
- Cookie:
HttpOnly/Secure/SameSite=Lax - 既定有効期限 8時間(業務時間想定)。OWNER/ADMIN は 4時間に短縮(テナント設定)
- アイドルタイムアウト 60分(無操作で再認証)
- 同時セッション: 端末別に許可、OWNER/ADMIN 画面で一覧と失効が可能
- 不審な IP / UA 変化検知でメール通知(後続)
失敗ロックアウト
- 同一アカウントで連続失敗 5回 → 15分ロック(指数バックオフ後続)
- ADMIN 操作で即時解除可能
認可(多層)
- テナント越境はミドルウェア + サービス層で二重チェック
- 要配慮情報の閲覧は 追加権限(OWNER の許可リストに登録された MEMBER のみ)
- break-glass: 例外的アクセス時には「理由」入力を必須化し、監査ログに残す
監査ログ
必須記録対象
- 認証成功 / 失敗 / MFA / ロックアウト
- ロール変更 / 権限変更 / メンバー招待・削除
- 要配慮データの閲覧 / 編集 / エクスポート
- ファイルのダウンロード(特に
confidential/sensitive) - 暗号化キー操作(KEK / DEK の生成・ローテーション)
- ADMIN によるテナント横断操作 すべて
- 設定変更(決済プロバイダ、LINE チャネル接続、外部連携)
スキーマ(暫定)
ts
audit_logs {
id: ulid (PK)
tenant_id: string | null // ADMIN 操作は null
actor_user_id: string
actor_role: 'OWNER' | 'MEMBER' | 'ADMIN'
action: string // 'customer.read_sensitive' 等
target_type: string // 'customer' / 'file' / 'tenant'
target_id: string
reason: string | null // break-glass 時
ip: string
user_agent: string
occurred_at: datetime
prev_hash: string // 前レコードのhash
hash: string // 自レコードのhash
}改ざん耐性
- append-only(更新・削除SQLを禁止、運用上は ADMIN でも不可)
- 各行に前行ハッシュを含めてハッシュチェイン化(後続実装)
- 一定間隔で外部ログ基盤(Logpush + R2 / 外部DWH)にエクスポート
ロギングと PII マスキング
- アプリログには PII を出さない:
- 氏名、電話、メール、住所、生年月日 → マスクまたは ID 参照
- エラーメッセージにDBの値をそのまま入れない
- リクエストログ: メソッド / パス / ステータス / 所要時間 / ユーザーID / テナントID / リクエストID
- ボディは原則ログしない。問題調査時は機密区分を判定して短時間のサンプル取得
通信路
- TLS 1.2+ 強制(Cloudflare 側で終端)
- HSTS 有効化
- 重要 Cookie:
SecureHttpOnlySameSite=Lax - Webhook: 必ず署名検証(LINE / Stripe / Square / Google)
- CORS: 同一オリジン以外の API 直叩きは原則拒否、ADMIN ツール用は明示許可リスト
レート制限と濫用対策
- 認証エンドポイント: IP + アカウントの両軸で制限
- 一斉配信 / エクスポート: テナント単位のクォータ
- LINE Webhook: チャネル単位の流入制限 + キュー
- ADMIN エンドポイント: 別レート(厳しめ)
バックアップと復旧
- D1 のバックアップ: Cloudflare 機能 + 日次論理ダンプを R2 別バケットへ
- バックアップは 暗号化された状態のまま保管
- 復旧テストを四半期に1回(後続運用)
- DEK のリストア: KEK で
dek_encを復号できるかを定期検証
データ保持と削除
- ソフトデリート(
deleted_at)→ 一定期間後に物理削除 - 保険業法・APPI に基づく保管期間 を検討:
- 顧客同意・苦情記録は所定の期間保管
- 期間経過後は安全な方法で削除(暗号化キー破棄による無効化も選択肢)
- ユーザー / テナントの削除リクエスト対応:
- 30日以内に削除完了
- 法令上保管必須のデータは仮名化して残す
脆弱性管理
- 依存ライブラリの定期更新(Dependabot / Renovate 後続)
- 既知CVEの監視
- 重要ライブラリ(Better Auth、Hono、Drizzle、Mermaid 等)はメジャー更新前にレビュー
- 静的解析(ESLint セキュリティルール、
gitleaks等)を CI に組込 - ペネトレーションテスト: ローンチ前 + 年次(後続)
インシデント対応
- 検知: 異常なログイン、データ大量取得、暗号化キー操作、外部からの脆弱性報告
- 封じ込め: 該当セッション失効、テナントロック、外部連携停止
- 調査: 監査ログ + アクセスログ + サーバログ
- 通知: 個人情報漏えい時は APPI 第26条 に基づく報告 / 本人通知
- 是正: 該当 DEK のローテーション、必要なら KEK ローテーション、再発防止
開発・運用ガバナンス
- 本番アクセスは ADMIN のみ、操作はすべて監査
- 開発環境では本番データを使わない(合成データ、もしくは仮名化)
- 秘密情報は Workers Secrets / 環境変数のみ。コード・ログ・PR に露出させない
- コードレビュー必須、セキュリティに関わる変更は必ずタグ付け
- リリース前のチェックリスト(暗号化要件、監査要件、入力検証)
チェックリスト
- 暗号化対象カラムをすべて
*_enc - 暗号化抽象 (
EncryptionService -
gitleaks
未確定
- 顧客マイナンバーの取得すべきかどうか