50_セキュリティ
本章では、filix-shadowwork-api におけるセキュリティ前提と、認証・署名・Secrets/PII の取り扱いを定義する。
ここで扱うのは「どのように安全性を確保するか」という設計上の前提であり、個別の実装詳細(ライブラリ呼び出し等)は対象外とする。
セキュリティ方針(前提)
-
最小権限(Least Privilege)
API の利用者・運用者・外部サービスに付与する権限は最小化する。 -
入力はすべて不正である前提
すべてのリクエスト入力を検証し、想定外のデータを拒否する。 -
外部サービスとの境界は署名/トークンで保護する
Webhook、管理 API、LLM 連携は必ず認証情報で境界を守る。 -
監査可能性(Auditability)
不具合や不正の切り分けのため、重要操作はログに残る設計にする(PIIを含めない)。
認証(Authentication)
目的
- 「このリクエストが誰のものか」を確実に特定する(なりすまし防止)
user_idの真正性を担保する
前提
- フロントエンドは外部の会員基盤(例: Memberstack)によりログイン状態を得る。
- バックエンドは、フロントエンドから提示される認証情報を検証して user_id を確定する。
認証方式(選定中)
現時点では認証方式は未確定とし、以下の要件を満たす方式を採用する。
方式が確定したら、本節に「採用方式」と「検証手順」を記載する。
必須要件
- 認証情報を検証し、
user_idをバックエンド側で確定できること - なりすましを防止できること(改ざん検知)
- リプレイ攻撃に対する耐性を持つこと(例: タイムスタンプ/nonce 等)
- 認証情報は HTTPS 経由でのみ送信されること
候補(例)
- 方式A: JWT
- フロントから JWT を送信し、バックエンドは署名検証して
user_idを取り出す。 - 送信経路は
Authorization: Bearer <token>とする。 - 方式B: 署名付きリクエスト
- フロントがリクエストに署名を付け、バックエンドが共有秘密で検証する。
- 署名対象は method/path/body/timestamp 等を含める(リプレイ耐性のため)。
移行方針
現状が「user_id を単純に渡すだけ」の場合、認証方式の導入を最優先の作業として扱う。
利用権限の前提(paid 等)
- 有料状態(paid)は
user_flags.paidを参照する。 - paid の更新経路は
40_課金と利用権限に従う。 - 認証により確定した user_id に対してのみ paid を参照する(任意の user_id 指定を許さない)。
Stripe Webhook の署名検証
目的
- Webhook の送信元が Stripe であることを保証し、偽装を防ぐ。
必須要件
- Stripe が付与する署名ヘッダ(例:
Stripe-Signature)を検証する。 - 検証に使用する秘密鍵(webhook secret)は 環境変数(Secrets) として安全に管理する。
- 検証失敗時は 4xx を返し、DB更新等の副作用を起こさない。
追加要件
- タイムスタンプ検証により、リプレイ攻撃の難易度を上げる。
- Webhook の冪等性(重複排除)は
40_課金と利用権限に従う。
管理用API(/api/admin/*)の保護
/api/admin/set_paid のような管理操作は、通常のユーザー操作と同等には扱えない。
誤用・悪用時の影響が大きいため、強い制限を必須とする。
必須要件
- 管理者のみ実行可能
- 管理者トークン、または環境変数で持つ共有秘密を用いて認証する。
- 本番環境では最小限
- 可能なら本番で露出させず、運用ツールや一時的な手段に閉じる。
追加要件
- IP 制限(Cloudflare Access / Zero Trust など)
- 監査ログ(誰がいつ何を変更したか)
- レート制限(ブルートフォース対策)
Secrets の管理
対象
- Stripe webhook secret
- LLM API key
- 管理者トークン(admin secret)
- その他、外部サービスの API key / secret
方針
- Secrets は リポジトリにコミットしない
- Cloudflare Workers の Secrets / Vars に格納する
- ログに出さない(例外メッセージに含めない)
PII(個人情報)の取り扱い
取り扱い対象
user_id(会員基盤の識別子)- ユーザー入力(シャドウワークの内容は機微性が高い)
方針
- 最小限の保存: 必要な範囲に限定して保存する
- ログには出さない: user_text / content をログに出さない
- アクセス制御: 認証された本人のデータのみ取得可能とする
- データ削除: 将来、ユーザー要求に応じた削除・退会対応を検討する(運用ポリシー)
入力検証(Validation)
- すべての API は入力を検証し、想定外の型・範囲・サイズは拒否する。
- 特に
content(ユーザー入力)はサイズ上限を設ける。 id(run/thread/message 等)は形式を統一し、受け入れ可能な文字種を限定する。
CORS とブラウザ公開
- ブラウザから API を呼ぶ場合、CORS を適切に設定する。
- Origin を必要以上に許可しない(固定のフロントURLを許可する)。
- 認証情報(Authorizationヘッダ等)を使う場合は、CORS で明示的に許可する。
参照
20_API仕様: 認証前提、エラー形式40_課金と利用権限: Webhook と paid 更新の冪等性60_利用制限と運用ポリシー: 悪用対応、運用上の制限