// docs / security guides / pre-launch SaaS
SaaS リリース前セキュリティチェックリスト: 35+ 項目
Cursor、Claude Code、Lovable、または Bolt で構築された SaaS 製品をリリースしてから数日以内です。このチェックリストは、AI ツールが常に見逃しているセキュリティ面と、迅速に出荷する創業者が顧客の資金を受け取る前に対処する必要があるセキュリティ面をカバーする go/no-go 監査です。 8 つのセクション、35 以上の項目があり、それぞれ 30 ~ 90 分で解決できます。印刷してバツ印を消して、自信を持って導入してください。
以下の各項目は必須です。緑色は出荷および検証済みを意味します。赤は未解決で起動をブロックしていることを意味します。コード スニペットと実際の失敗パターンを含む各カテゴリの詳細なチュートリアルについては、How to secure an app built with AI coding tools および The vibe coding security checklist を参照してください。
顧客データの分離
マルチテナント SaaS では、最初のセキュリティ境界はデータの分離です。すべての顧客のデータは、他のすべての顧客に到達できないようにする必要があり、これはアプリケーション層ではなくデータベース層で適用されます。
ALTER TABLE public.table_name ENABLE ROW LEVEL SECURITY; ALTER TABLE public.table_name FORCE ROW LEVEL SECURITY;を含むすべての Supabase テーブルで行レベルのセキュリティ (RLS) を有効にします。 FORCE は、テーブル所有者がそれをバイパスできないようにします。- RLS ポリシーごとに、述語のスコープが認証されたユーザーまたは組織であることを確認します。例:
CREATE POLICY "users_see_own" ON public.items FOR SELECT USING (auth.uid() = user_id);。 2 番目のユーザー アカウントでテストして、データが分離されていることを確認します。 - Firebase / Firestore を使用する場合、ルールはテナント モデルと一致する必要があります。
allow read, write: if true;または時間制限のあるテスト ルールは使用しないでください。allow read, write: if request.auth.uid == resource.data.owner_uid;または同等の組織スコープの一致を使用します。 - ファイル アクセスには署名付き URL または有効期限の短いトークンを使用し、公開バケットは使用しないでください。 Supabase ストレージ:
objectsテーブルにENABLE ROW LEVEL SECURITYを設定し、ファイル アクセスの範囲を認証されたユーザーに限定するポリシーを作成します。別のユーザーとしてダウンロードをテストします。 - API レイヤーでは、すべてのリクエストに
auth.uid()または org-id コンテキストが含まれている必要があります。すべてのデータベース クエリは、そのコンテキストによってフィルタリングする必要があります。例: いいえSELECT * FROM items WHERE id = $1;いつもSELECT * FROM items WHERE id = $1 AND user_id = auth.uid()。
請求と支払い
Stripe の統合では、顧客の信頼と財務上の安全性が両立します。ここでの構成ミスとは、支払いの盗難、返金ループ、または収益の損失を意味します。
- 運用環境では live Stripe keys を使用します。ステージングでは別のテストモード キーを使用してテストします。最終的なライブ モード スキャンを行わずに、テストからライブにスイッチを切り替えないでください。
- すべての受信イベントで Webhook 署名を確認します:
const event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);。署名が失敗した場合にスローします。 Webhook シークレットは環境変数にのみ保存し、コードには保存しないでください。 event.idをキーとするデータベース テーブルを使用して、Webhook ハンドラーに冪等性を実装します。同じ Webhook が 2 回到着した場合 (実際に到着することになります)、2 回目の実行は何も行われません。状態変更と同じトランザクション内で冪等行を書き込みます。customer.subscription.updatedとcustomer.subscription.deletedでは、すぐにアクセスを取り消します。 cron を待たないでください。 Stripe ダッシュボードでサブスクリプションをキャンセルし、ユーザーが 5 秒以内にロックアウトされることを確認してテストします。- Stripe 顧客 ID とサブスクリプション ID のみをデータベースに保存し、完全なカードや API キーは保存しないでください。すべての認証境界 (ページの読み込み、API 呼び出し、cron チェック) ごとに Stripe からライブ サブスクリプション状態を取得します。サブスクリプション ステータスを 1 分以上キャッシュしないでください。
認証とセッション
Auth は、SaaS における二次攻撃者のターゲットです。ユーザー アカウントは、データと支払い方法へのベクトルです。
- すべての保護されたルートで
supabase.auth.getUser()を使用し、getSession()を使用しないでください。getSession()は未検証の Cookie を読み取ります。getUser()は、JWT サーバー側を検証します。 Next.js: 保護されたコンテンツを提供する前にconst { data: { user } } = await supabase.auth.getUser();。 - 認証 Cookie に
SameSite=Laxを設定します (Supabase 認証はデフォルトでこれを行います)。 「DevTools」→「Application」→「Cookie」で確認します。SameSite=Noneが表示される場合は、セッション構成にsameSite: 'Lax'を追加します。 - 自分の管理者アカウントで MFA を有効にします。ユーザー向けの MFA の場合は、起動前にエンドツーエンドでテストします。サインアップし、TOTP デバイスを登録し、サインアウトして、TOTP トークンを使用して再度サインインし、動作することを確認します。
- Magic-link トークンは 15 分以内に期限切れになる必要があります。パスワード リセット トークンは 1 時間以内に期限切れになる必要があります。セッション トークン (JWTs) はより長く存続できます (24 時間~7 日) が、使用するたびに検証する必要があります。認証プロバイダーのデフォルトを確認してください。
- サインアウトの完全性をテストします。ユーザーがサインアウトをクリックすると、ブラウザーは認証セッションを削除し、サーバーはトークンをすべて取り消し、ユーザーは保護されたページにアクセスできなくなります。 Supabase:
await supabase.auth.signOut()を呼び出し、JWT が次のリクエストで有効でなくなっていることを確認します。
PII とコンプライアンス
電子メール、名前、支払い情報、または PII を収集する場合は、データの最小化、安全な保管、要求に応じた削除、DPA への準備などの法的義務があります。
- プライバシー ポリシーを作成して公開します (インディー SaaS の場合でも、オプションではありません)。収集するデータ、その理由、保管期間、およびユーザー権限 (アクセス、修正、削除) を明記します。 Termly などのテンプレートを使用しますが、それをカスタマイズします。
- PII をデータベースから削除するアカウント削除 API エンドポイントを実装します。テストしてください: アカウントを作成し、データを追加し、アカウントを削除し、データがなくなっていることを確認します (直接データベース検査を使用します)。
- GDPR / CCPA の遵守のため、データ主体のリクエスト (アクセス/修正/削除) には 30 日以内に対応してください。プロセスを文書化します。アプリが EU- ベースであるか、EU ユーザーにサービスを提供する場合、Stripe、Supabase、および任意のプロセッサを備えたデータ Processing 付録 (DPA) が必要です。
- 保管時の機密フィールドを暗号化します (パスワードは認証プロバイダーによってハッシュされますが、クレジット カードのトークン化、API キー、シークレットには
pgcryptoまたは外部ボールトを使用する必要があります)。プレーンテキストのクレジット カード番号は決して保存しないでください (代わりに Stripe トークン化を使用してください)。
運用準備状況
セキュリティは継続的です。インシデント対応、監視、運用手順書は初日より前から始まります。
- ステータス ページ (Statuspage.io、Uptime Robot、または単純な index.html) を設定します。顧客は、停電が発生しているかどうかを知る必要があります。事件ごとに更新してください。
- オンコールローテーションを文書化してテストします。午前2時の警報で起きる人は誰でしょうか?デプロイキーを持っているのは誰ですか?侵害されたトークンを取り消すことができるのは誰ですか?それを文書化し、起動前にファイアドリルを実行します。
- 顧客が侵害を報告した場合、キーを紛失した場合、サービスがダウンした場合にどうするかについて、セキュリティ インシデント対応ランブックを作成します。それをチームに配布します。 1 つのシナリオ (e.g.、キーのリークをシミュレート) をテストして、計画が機能することを確認します。
- バックアップと復元の手順は、理論上ではなくテストする必要があります。バックアップから復元できますか?時間を計ってください。 Supabase: 自動バックアップを有効にします (無料の場合は 7 日間、有料の場合は 30 日間の保持)。四半期ごとに別のプロジェクトへの復元をテストします。
- 特権操作の監査ログを有効にします: Stripe ダッシュボード ログイン、Supabase 管理者 API 呼び出し、データベース スキーマの変更、支払い調整。ツール: CloudTrail (AWS)、Supabase 監査ログ、PostgreSQL
pgaudit拡張機能。
外部の攻撃対象領域
あなたの API 境界は攻撃者による継続的なスキャン下にあります。悪意のあるトラフィックが発生する前にロックダウンします。
- すべてのパブリック エンドポイントのレート制限。例: サインアップ時は IP ごとに 1 分あたり 100 リクエスト、パスワード リセット時は 1 分あたり 10 リクエスト。 Vercel KV、Redis などを使用します。 429 (リクエストが多すぎます) で失敗します。
- CAPTCHA (hCaptcha または reCAPTCHA) をサインアップおよびパスワード リセットのエンドポイントに追加して、ボットを阻止します。リクエストを受け入れる前に、サーバー側のトークンを確認してください。
- 利用可能な場合は WAF (Web アプリケーション ファイアウォール) を使用します: Cloudflare、Vercel Web アプリケーション ファイアウォール、または AWS WAF。既知の悪意のある IP とパターンを自動的にブロックします。
- 開いている API エンドポイントをスキャンします。運用ドメインに対してパッシブ FixVibe スキャンを毎月実行します。公開されたデバッグ ルート、GraphQL イントロスペクション、API キーの漏洩、または構成の公開に関する調査結果を確認します。
- 資格情報 (API キー、OAuth トークン、データベース パスワード) を四半期ごとにローテーションします。ローテーション手順を文書化し、可能な場合は自動化します。
可観測性とロギング
問題が発生した場合、ログがフォレンジック記録となります。初日からセットアップしてください。
- ログを一元化: Supabase ログ、Vercel ログ、アプリケーション ログ、認証ログを 1 つのダッシュボード (Datadog、LogRocket、または自己ホスト型 ELK) に集約します。検索可能、少なくとも 90 日間保持されます。
- セキュリティ イベントに関するアラート: 繰り返されるログインの失敗 (アカウント乗っ取りの可能性)、異常な API の使用 (スクレイピングの可能性)、エラーの急増 (攻撃または正当なインシデントの可能性)。しきい値と Slack 統合を設定します。
- ユーザー役割の変更、新しい管理者アカウントの作成、支払い方法の追加、API キーの範囲の変更など、あらゆる特権操作の監査ログを出力します。これらをアプリケーション ログとは別に、不変の保持で保存します。
最終確認
発表する前に、FixVibe の完全スキャンを実行し、セキュリティの観点から結果を確認してください。
- 運用ドメインに対して FixVibe Pro アクティブ スキャンを実行します。アクティブなテスト用にドメインを設定します (DNS TXT または HTTP ファイル検証)。スキャンを承認し、すべての検出結果、特に重大かつ重大度の高いものを確認します。それぞれを修正するか、明示的に受け入れます。
- スケジュールされた再スキャンを有効にします: Pro プラン → 3 時間、6 時間、12 時間、または毎日。 Unlimited プラン → 6 時間、12 時間、毎日、2 日ごと、または毎週。検証済みドメインの active IDOR walking、SQL injection、reflected XSS チェックと組み合わせます。
- Webhook を構成します。FixVibe を Slack または電子メールに接続して、重要な検出結果がリアルタイムでアラートをトリガーできるようにします。セットアップについては /docs/webhooks を参照してください。
- /docs/security-guides/ai-generated-code-security-scanner の注意点、つまりバンドル内のシークレット、RLS/rules、認証境界、CSP、ミドルウェアの配置に焦点を当てた最終的な手動コード レビューを実行します。 vibe coding security checklist をレビュー テンプレートとして使用します。
打ち上げ日
チェックリストをクリアしました。自信を持って導入してください。起動後は積極的に監視します。最初の 1 週間は FixVibe の結果を毎日チェックし、アラートには 1 時間以内に応答し、スキャン スケジュールを実行し続けます。コード スニペットを含む段階的な強化ガイドについては、How to secure an app built with AI coding tools を参照してください。
