ClaudeやCursorでアプリをコード生成して、有料クライアントに引き渡そうとしている。ちょっと待て。AI生成コードには予測可能なセキュリティ穴がある — 露出したAPIキー、入力検証の欠落、デフォルトのデータベース権限で誰もが他のユーザーのデータを見ることができる。これはエッジケースではない。ほぼすべてのAI生成コードベースの最初のパスで発生する。
このガイドはローンチ前のセキュリティチェックリストだ。実際のユーザーがアプリに触れる前に、ステップバイステップで従うこと。最も一般的なバイブコーディングスタック(Next.js + Supabase + Vercel)向けに書かれているが、原則はツールに関係なく適用できる。
AI生成コードがセキュリティ問題を持つ理由
AIモデルは「動作するか」を最適化する。「セキュアか」ではなく。「タスクマネージャーをユーザーアカウント付きで構築して」とClaudeに言うと、ユーザーを作成し、タスクを保存して、表示するコードが生成される。ただし自動的にはしないこと:ユーザーAがユーザーBのタスクを見ることができないようにする、入力フィールドが悪意のあるスクリプトを受け入れられないようにする、ブラウザの開発者ツールからAPIキーを隠す、またはエンドポイントへのハンマリングを防ぐレート制限を追加する。
これはAIの失敗ではない — プロンプトのギャップだ。AIは要求されたものを構築する。セキュリティを求めなかった可能性が高い。機能に焦点を当てていたから。今度は戻ってそれを追加する時間だ。
ステップ1:環境変数を監査する
これはバイブコードアプリで最も一般的で最も危険な間違いだ。プロジェクト内のすべてのファイルでハードコードされたAPIキー、データベースURL、またはシークレットをチェックする。
探すもの:sk-、eyJ、sbp_、supabase、postgres://、または長いランダムに見える文字列で始まる文字列をコードベース内で検索する。特にこれらのファイルをチェック:/appまたは/pagesディレクトリ内の任意のファイル、任意のコンポーネントファイル、next.config.js、および任意のユーティリティファイル。
修正:すべてのシークレットを環境変数に移動する。Next.jsでは、NEXT_PUBLIC_でプレフィックスされた変数のみがブラウザに公開される。データベースURL、サービスロールキー、APIシークレットはこのプレフィックスを持つべきではない。
# .env.local(このファイルをコミットしないこと)
SUPABASE_SERVICE_ROLE_KEY=your-secret-key
DATABASE_URL=postgres://...
# これらはブラウザに公開しても大丈夫:
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
確認:.gitignoreファイルに.env.localが含まれているかチェックする。既にシークレットをGitにコミットしている場合、削除後も履歴に存在する — 露出したキーをすべて即座に交換(再生成)する。
ステップ2:Supabaseで行レベルセキュリティを有効にする
Supabaseを使用している場合、これは最も重要なステップだ。デフォルトでは、Supabaseテーブルはアクセス制限がない — 匿名キーがあれば、誰もがすべてのテーブルのすべての行を読み書きできる。つまり、ユーザーAは単純なAPI呼び出しでユーザーBのデータを見ることができる。
修正:すべてのテーブルで行レベルセキュリティ(RLS)を有効にし、アクセスを制限するポリシーを作成する。
Supabaseダッシュボード → テーブルエディタ → 各テーブルを選択 → 「RLS無効」をクリックして有効にする。次にポリシーを追加:
ユーザーが独自のデータのみを見るべき一般的なアプリの場合、SELECTポリシーを作成する:auth.uid() = user_id。INSERT、UPDATE、DELETEに対して同様のポリシーを作成する。
テスト:ユーザーAとしてログインし、APIを通じてユーザーBのデータにアクセスしてみる。それが見えたら、ポリシーが間違っている。Supabaseにはポリシーを直接テストできるSQLエディタがある。
AI一般的な間違い:Claudeはしばしばservice_roleキー(RLSをバイパス)を使用してSupabaseクエリを生成する。anonキーの代わりに適切なRLSポリシーで。クライアント側のコードが匿名キーのみを使用していることを確認する。サービスロールキーはサーバー側のコード(APIルート、サーバーアクション)にのみ存在し、ブラウザに公開されるべきではない。
ステップ3:認証を適切に追加する
AI生成の認証コードはしばしば機能するが、ショートカットを取る。これらの特定の問題をチェック:
セッション管理:セッションが期限切れになることを確認する。認証設定に合理的なセッションタイムアウトが含まれていることを確認する(Supabaseのデフォルトは一般的に問題ないが、検証する)。ログアウトがローカルクッキーをクリアするだけでなく、実際にセッションを無効化することを確認する。
パスワード要件:メール/パスワード認証がある場合、最小パスワード長を適用する(8文字以上)。Supabaseはプロジェクト設定 → 認証 → パスワード要件でこれを処理する。
保護されたルート:ユーザー固有のデータを表示するすべてのページには認証ミドルウェアが必要だ。Next.jsアプリルータでは、有効なセッションをチェックしてファイル認証されていないユーザーをログインページにリダイレクトするミドルウェアを作成する。クライアント側のチェックのみに依存しない — ユーザーはAPIに直接ヒットすることでそれをバイパスできる。
メール検証:Supabase認証設定でメール確認を有効にする。これにより、人々が偽のメールアドレスでアカウントを作成することを防ぎ、基本的なアカウント妥当性のレイヤーを追加する。
これから価値を得ている?AIツール、ワークフロー、実践的ガイドに関する詳細を週1回発行している。最初に得るリーダーに参加 →
ステップ4:すべての入力を検証する
AI生成フォームは通常、基本的な検証(必須フィールド、メール形式)を持つが、悪意のある入力からめったに保護しない。
追加するもの:
すべてのAPIエンドポイントでサーバー側検証。クライアント側検証のみに信頼しない — APIに直接リクエストを送信することでバイパスできる。Zodなどの検証ライブラリ(TypeScript向け)を使用して、アプリが受け入れるすべてのデータのスキーマを定義する。
ブラウザで表示されるユーザー生成コンテンツ内のHTMLをサニタイズする。アプリにコメント、説明、またはブラウザにレンダリングされるテキストフィールドがある場合、DOMPurifyなどのライブラリを使用して危険なスクリプトを削除する。これなしでは、誰かが他のユーザーのセッションを盗むJavaScriptを注入できる(クロスサイトスクリプティング / XSS)。
ファイルアップロードサイズと種類を制限する。アプリがファイルアップロードを受け入れる場合。AI生成アップロードハンドラーはしばしば制限がない。誰かが2GBファイルまたは実行可能ファイルをアップロードできることを意味する。サイズ制限を追加する(5MBはほとんどのアプリで合理的)とファイル種類を制限する。実際に必要なもの(画像、PDF、など)。
ステップ5:APIルートをセキュアにする
アプリ内のすべてのAPIルートまたはサーバーアクションをこれらの問題でチェック:
すべてのエンドポイントの認証。ユーザーデータを返すまたは変更するすべてのAPIルートは、最初にユーザーのセッションを確認すべき。AIはしばしば誰からのリクエストも受け入れるAPIルートを生成する。
認証を超えた認可。ユーザーがログインしていることを確認した後でも、特定のリソースへのアクセスが許可されていることを確認する。「ユーザーAはログインしている」は「ユーザーAはユーザーBのプロフィールを編集できる」を意味しない。すべてのデータアクセスで所有権をチェックする。
レート制限。レート制限がないと、誰かが1秒あたり数千のリクエストをAPIに送信して、データをスクレイプしたり、サーバーを圧倒することができる。rate-limiter-flexibleなどのライブラリを使用して基本的なレート制限を追加するか、Vercelの組み込みレート制限をエッジ関数で使用する。
HTTPメソッド。APIルートが応答すべきHTTPメソッドのみに応答することを確認する。POSTリクエストを処理するルートは明示的に設計した場合を除き、DELETEリクエストに応答すべきではない。
ステップ6:デプロイメント構成をチェックする
デプロイメントプラットフォームにはAIが構成しないセキュリティ設定がある。
チェックするVercel設定:「デプロイメント保護」を有効にする(プレビューデプロイメントを表示するには認証が必要 — クライアントが誤ってプレビューURLを共有して進行中の作業を公開することを防ぐ)。予期しない料金を防ぐために支出制限を設定する。CORS設定で許可されたドメインを構成する。
カスタムドメインとSSL:クライアントにこれを配信する場合、カスタムドメインをHTTPSで設定する。VercelとNetlifyはSSLを自動的に処理する。クライアントアプリを.vercel.appサブドメインに配信しない — それは非専門的に見える、クライアントが簡単に転送することはできない。
ヘッダー:next.config.jsまたはvercel.jsonにセキュリティヘッダーを追加:X-Content-Type-Options: nosniff、X-Frame-Options: DENY(サイトがiframeに埋め込まれることを防ぐ。クリックジャッキング)、Strict-Transport-Security(HTTPSを強制)。これらは攻撃のクラス全体を防ぐ1回限りの追加。
ステップ7:最終的なセキュリティスキャンを実行する
クライアントに何かを渡す前に、これらの無料チェックを実行:
npm audit:プロジェクトディレクトリでnpm auditを実行する。依存関係の既知の脆弱性にフラグを立てる。重大度「critical」と「high」の問題を修正する。自動修正が利用可能な場合はnpm audit fixを実行する。
Lighthouse:デプロイされたサイトをChromeで開き、DevToolsを開き、Lighthouse監査を実行する。「ベストプラクティス」スコアをチェック — HTTPS欠落、脆弱なライブラリ、セキュアでないヘッダーなどの一般的なセキュリティ問題をキャッチする。
手動テスト:1人のユーザーとしてログインし、別のユーザーのデータにURLまたはAPI呼び出しを変更してアクセスしてみる。空のフォーム、サイズが大きすぎる入力、特殊文字(エンコードされたXSSペイロードなど)を送信してみる。これらのいずれかが機能する場合、修正する問題がある。
ローンチ前チェックリスト
これを印刷して、ライブになる前にすべてのアイテムをチェック:
- すべてのシークレットが環境変数に(ハードコードされたキーなし)
.env.localが.gitignore内(およびGit履歴にシークレットなし)- Supabase RLSがすべてのテーブルで有効
- RLSポリシーがテスト済み(ユーザーAはユーザーBのデータを見ることができない)
- クライアント側コードは匿名キーのみを使用(サービスロールキーはサーバー側のみ)
- すべてのAPIルートの認証
- 認可チェック(データアクセスの所有権検証)
- すべてのフォームの入力検証(クライアント側だけでなく、サーバー側)
- ファイルアップロード制限(該当する場合、サイズと種類)
- APIエンドポイントのレート制限
- セキュリティヘッダーが構成済み
npm audit実行済みで重大度「critical」の問題が修正- カスタムドメインでSSL構成済み
- プレビューデプロイメントが保護済み
- ユーザー間データアクセステストに合格
結論
バイブコードアプリのセキュリティ保護はセキュリティの専門家になることではない。AIが残す予測可能なギャップをキャッチするチェックリストを実行することだ。上記のステップは2~4時間かかり、最も一般的な脆弱性を防ぐ。クライアント向けアプリの場合、これはオプションではない — それは専門的な仕事と責任の違いだ。
最初のバイブコードアプリを構築しているのなら、バイブコーディングの完全ガイドで始める。ClaudeやCursorで使用するプロンプトを改善したい場合は、無料プロンプト最適化ツールを試す。最高のコーディングツールの内訳については、Claude Code vs Codexを参照する。
これは毎週することだ。AIツール、ワークフロー、正直な意見に関する詳細 — ハイプなし、フィラーなし。参加 →
開示:この記事のいくつかのリンクはアフィリエイトリンク。個人的にテストして定期的に使用するツールのみを推奨する。完全な開示ポリシーを参照する。