なぜ危ないのか
AIはプロンプトで明示されない限り、デモや動作確認の目的でSupabaseのanon keyとプロジェクトURLを直接コードに書き込む。生成時の意図は「動くサンプル」を作ることだが、ユーザーがそのまま共有すると実際のデータベースに接続できるキーが外部に流出する。AIは生成物のセキュリティリスクを自動で警告しないため、ユーザーが自分でガードする仕組みを持つことが重要だ。
anon keyはJWT形式であり、デコードするとSupabaseのプロジェクト識別子やロール情報が読み取れる。HTMLのソースを「ソースを表示」で確認するだけで取得できるため、技術的なハッキング手法を使わなくても漏えいが成立する。漏えい後にRLSが不十分だと判明しても、その時点ではすでにアクセスが発生している可能性がある。
- 相手がログインなしで開ける状態か確認する
- PCとスマホで最低1回ずつ表示を確認する
- 内部情報・個人情報・不要な外部送信が残っていないか見る
- レビュー期限と修正時の差し替え方を決めておく
ソースで見る場所
生成されたHTMLに対してgrepコマンドでスキャンする。`grep -n 'eyJ' index.html` を実行し、JWTが含まれているか行番号付きで確認する。`supabase.co` を含む行も合わせて確認し、接続情報が一式ハードコードされていないかをチェックする。このスキャンは生成のたびに実行する習慣をつけるとよい。
複数のHTMLファイルを一度にスキャンする場合は `grep -rn 'eyJ\|supabase\.co' ./output/` のようにOR検索を使う。`-r` で再帰検索、`-n` で行番号表示になる。出力がゼロであれば対象ディレクトリ内にSupabase関連のキーが含まれていないことが確認できる。このコマンドをMakefileやjustfileに登録しておくと毎回タイプする手間が省ける。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
安全に共有する設定
プロンプトの先頭に「接続情報(URL・anon key)は文字列として記述せず、それぞれ`SUPABASE_URL`・`SUPABASE_ANON_KEY`というマーカーを使うこと。実際の値は絶対に書かないこと。」という制約文を加える。この一文がある場合、AIは`createClient('SUPABASE_URL', 'SUPABASE_ANON_KEY')`という形式でコードを生成し、実際のキーを出力しなくなる。
生成後のスキャンで問題なければ、認証付きプレビューリンクで共有する。スキャンでマーカー文字列(`SUPABASE_URL`など)が検出された場合は想定どおりの状態であり、共有しても実際の接続先は特定されない。スキャンで予期せぬ `eyJ` が検出された場合は、プロンプトの制約が効かなかったケースとして該当箇所を修正してから共有する。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
再発防止ルール
プロンプトテンプレートをGitリポジトリまたは共有ドキュメントに保存し、Supabase接続HTMLを生成する際は必ずそのテンプレートから始める運用を徹底する。各自がゼロからプロンプトを書くと制約を入れ忘れるリスクがあるため、テンプレート使用を必須プロセスにする。
スキャンコマンドをjustfileやMakefileの `check-secrets` ターゲットとして登録し、`just check-secrets` で実行できるようにする。HTMLを共有フォルダに移す前にこのコマンドを実行することをチームのルールとして明文化する。CI/CDがある場合はプルリクエスト時に自動実行されるよう組み込む。
- HTML内の外部script・form action・iframeを確認する
- APIキーやトークンらしき文字列がないか検索する
- 画像・CSS・JSのパスが公開後も解決できるか見る
- noindexと認証を混同せず、必要なら両方設定する
よくある質問
プロンプトでマーカーを指示してもAIが実際のキー値を生成することはありますか?
制約を明示しても長い会話の途中で指示が薄れ、AIが実値を生成するケースが稀にある。そのためプロンプト制約だけに頼らず、生成後のgrepスキャンを必ず実施することが重要だ。二重確認が実質的なゼロリスクを担保する。
grepスキャンをCIに組み込む最も簡単な方法は何ですか?
GitHub Actionsであれば、ジョブに `run: grep -rn 'eyJ' ./public/ && exit 1 || exit 0` を追加するだけで検出時にCIを失敗させられる。gitleaksを使う場合は公式アクション `trufflesecurity/trufflehog-actions-scan` を追加すると詳細なレポートも得られる。
Supabase以外のバックエンドのキーも同じ方法でスキャンできますか?
JWTを使うサービスは `eyJ` 検索で一括して検出できる。Firebase・Auth0なども同形式のトークンを使うため有効だ。サービス固有のパターン(Stripeの`sk-live_`など)は別途キーワードを追加してスキャンする。