Security 2026年8月28日

第10回 デプロイ基盤の本番前チェック — Cloudflare / Vercel / Supabase 横断で見る

公開直前に3つの基盤を同じ観点で点検するための1枚の表を作ります。秘密情報の分離、環境の分離、公開範囲、鍵の種類、流入制御、短命な認証情報という6観点を、Cloudflare / Vercel / Supabase の3列で並べ、無料枠で何ができるかまで正直に書きます。

難易度基本的な操作を一度試したことがある前提です種別学習コース

この回のキーメッセージ

プラットフォームは「デフォルトで安全」ではなく「安全にできる」だけです。公開直前に必要なのは各基盤の使い方の再学習ではなく、3つの基盤を同じ6つの観点で横に並べた1枚の点検表です。

第7回から第9回までは、生成されたコードそのものをどう読むかを扱いました。ここからは視点を外へ移します。コードが正しくても、それを載せる基盤の設定が抜けていれば結果は同じだからです。むしろ AI に実装を任せる比率が上がるほど、事故は「コードの誤り」より「設定の空欄」として現れます。エージェントはアプリケーションコードは書きますが、ダッシュボードのトグルは押しません。

この回で作るのは、公開ボタンを押す直前に上から順に見ていく1枚の表 です。Cloudflare / Vercel / Supabase のどれを使っているかで見る場所は変わりますが、確認したい問いは3つとも同じ です。ですから章はプラットフォームごとではなく、観点ごとに切ります。1つの観点につき3列を並べ、「どこで設定するか」「無料枠で足りるか」「どう確認するか」を書きます。

各プラットフォームの使い方そのものは、すでに入門コースと安全度マップで扱っています。ここでは手順を一から説明せず、必要な箇所でそちらへ渡します。

目的参照先
Cloudflare の本番公開・環境分離・シークレットの手順公開して運用する(Cloudflare)
Vercel の Deployment Protection の各方式管理画面を守る(Vercel)
Vercel の環境変数・ドメイン運用公開して運用する(Vercel)
Supabase の運用・ブランチング・バックアップ公開して運用する(Supabase)
機能ごとの安全度とプラン依存Vercel 安全に使える領域マップ / Supabase 安全に使える領域マップ

1. 点検表の骨格 — 6つの観点

先に全体像を置きます。以下の6つが、3つの基盤に共通して「設定しなければ守られない」箇所です。

#観点抜けたときに起きること
1秘密情報がコードとビルドから分離されているかリポジトリ・ビルドログ・クライアントバンドルへの鍵の混入
2本番と検証が分かれているかプレビュー環境が本番のデータを書き換える
3公開範囲が意図通りか未完成の管理画面や検証用データが認証なしで見える
4認証情報の種類を取り違えていないか管理者権限の鍵がブラウザへ配布される
5外部からの流入をどこで絞るか総当たり・スクレイピング・課金の急増を止める場所がない
6短命な認証情報に置き換えられるか漏れた鍵が失効するまで有効なまま残る

順番には意味があります。1〜2は「作るとき」に決まってしまい、後から直すとローテーションが必要になります。3〜4は公開の瞬間に効きます。5〜6は公開後に効いてくるもので、最初から完璧である必要はありませんが、どこに入れるかだけは決めておく必要があります。

以降、この順に見ていきます。

2. 観点1 — 秘密情報がコードとビルドから分離されているか

問いは「鍵がリポジトリに無いか」だけではありません。ビルドステップに渡っているかダッシュボードから読み出せるか も同じ観点に含まれます。ビルド時に参照できる値はビルドログに出得ますし、読み出せる値は管理アカウントが侵害されたときに読み出されます。

確認事項CloudflareVercelSupabase
本番の保管場所wrangler secret put(Worker ごと、暗号化・write-only、作成後は値を閲覧不可)環境変数に Sensitive フラグ を付与DB から参照する秘密は Vault(Postgres 拡張、DB 内に暗号化保存)
読み出せるか不可Sensitive なら不可。ダッシュボード再表示・vercel env pull も不可Vault は復号関数経由での参照
ビルドへ渡るかWorker 実行時のバインディングとして注入Sensitive はビルドステップに渡らず、関数・サーバーコンポーネント実行時のみ注入DB 関数・トリガー・pg_cron・Edge Functions から参照
ローカル開発.dev.vars / .dev.vars.<environment-name>.gitignore 必須)vercel env pull(Sensitive は取得できない前提で設計する)ローカルの環境変数ファイル。.gitignore 必須
よくある取り違えWrangler 設定の vars に機密を書く(vars は秘密の置き場ではない)Sensitive フラグを付けずに登録するクライアントに配る鍵と管理用の鍵を同じ場所で管理する

3つに共通する運用上の注意を2つ挙げます。

1つ目は、シークレット更新がデプロイを伴う場合があることです。 Cloudflare の wrangler secret put は Worker の新しいバージョンを即座にデプロイします。ローテーションのつもりで実行したコマンドが、まだ検証していないコードを本番へ出す可能性があります。段階的デプロイの途中で秘密だけを更新したい場合は wrangler versions secret put を使います。

# 値を更新すると同時に新バージョンが即デプロイされる
wrangler secret put RESEND_API_KEY

# 新バージョンを作るが、デプロイの進め方は別途制御する
wrangler versions secret put RESEND_API_KEY

複数の Worker で同じ鍵を使い回している場合、Worker ごとの Secrets とは別に、アカウントレベルで再利用できる Secrets Store があります。ただしベータのため、採用するかは自プロジェクトの安定性要件と照らして判断してください。置き場所の使い分けと設定手順は 公開して運用する(Cloudflare) にまとまっているので、本番前チェックではその記事のチェックリストと突き合わせてください。

2つ目は、既定値が安全側でないことです。 Vercel の Sensitive フラグは、付けなければ付きません。プロジェクト単位で強制する設定が Settings > Security & Privacy にあり、新規の Production / Preview 変数を自動的に Sensitive にする、--no-sensitive の指定をブロックする、といったポリシーを置けます。個々の登録操作の丁寧さに頼らず、組織の設定として強制する のが実務的な解です。エージェントに vercel env add を実行させる運用なら、なおさらプロジェクト側で固定しておくべきです。

Supabase の Vault は、アプリケーションから読む環境変数の代替ではなく、DB の中で完結する処理が使う秘密 の置き場だと理解すると誤りません。pg_cron から外部 API を叩く、トリガーから通知を送る、といった処理のトークンがここに入ります。

3. 観点1の補足 — 2026年4月の Vercel インシデントが示したこと

この観点を「フラグを1つ付けるだけの話」と軽く扱わないために、実際に起きたことを1件見ておきます。

2026年4月、Vercel は自社のセキュリティインシデントを公表しました。経路は Vercel 自身の製品の脆弱性ではなく、Context.ai というサードパーティ AI ツールの Google Workspace OAuth アプリが侵害され、そこから Vercel 従業員アカウントへ侵入が広がったものです。結果として、一部の顧客で非 Sensitive の環境変数が列挙され、復号されました。API キーや DB 認証情報が含まれます。Sensitive フラグの付いた環境変数、npm パッケージ、Next.js や Turbopack といった OSS プロジェクトは影響を受けていないと発表されています。Vercel は全顧客に対し、Sensitive でない環境変数の秘密もローテーションすること、MFA を有効にすること、アクティビティログを確認することを推奨しました。

ここから読み取るべきことは3つあります。

  • フラグを立てていない秘密は、平文で復号できる設計だった。 「マネージドサービスに預けたから暗号化されている」という理解は、この件では成り立ちませんでした。守られていたのは Sensitive を付けた分だけです。
  • 侵入経路は自分たちのコードではなかった。 OAuth で連携したサードパーティアプリが起点です。連携アプリの棚卸しはプラットフォーム設定と同じ列に置くべき項目で、これは第11回のサプライチェーンの話とつながります(秘密情報とサプライチェーン)。
  • ローテーションの手順が事前にないと、告知を受けても動けない。 「どの鍵が、どこに、いくつあるか」の一覧がなければ、推奨されたローテーションは実行できません。

この記事の点検表で観点1を先頭に置いているのは、ここが唯一「後から直すのに他人の時間まで巻き込む」項目だからです。公開範囲の設定は後からトグルを押せば直りますが、漏れた鍵は差し替えるしかありません。

4. 観点2 — 本番と検証が分かれているか

見るべきは3点です。環境ごとに変数が分かれているか接続先が分かれているか、そして プレビューが本番のリソースを触っていないか です。3点目が最も見落とされます。変数の欄が環境ごとに分かれていても、プレビュー欄に本番の接続文字列が入っていれば分離されていません。

確認事項CloudflareVercelSupabase
環境ごとの秘密.dev.vars.<environment-name> / .env.<environment-name> でローカル側を分離Production / Preview / Development の3スコープ+全体適用本番プロジェクトと検証プロジェクトを分ける、またはブランチング機能を使う
接続先の分離バインディング先の KV / D1 / R2 を環境ごとに変える環境ごとに接続文字列を別値で登録するプロジェクトが分かれていれば接続先も分かれる
落とし穴環境の設定構造は wrangler 設定の書き方に依存する。プロジェクトの設定ファイルで実際の割り当てを確認することPreview スコープに本番の値をコピーしてしまう検証用データを本番プロジェクトに入れて、後で消し忘れる

Cloudflare の環境ごとの構成については、wrangler 設定でどう環境セクションを切るかがプロジェクトごとに異なります。ここで一般化した手順を書くよりも、公開して運用する(Cloudflare) と自プロジェクトの設定ファイルを突き合わせるほうが確実です。

確認方法として実効性があるのは、検証環境から書き込みを1回行い、本番データに現れないことを目視する ことです。設定画面を眺めるだけでは、値が正しく分かれているかは分かりません。第9回で扱った「壊れることを確認する」テストと同じ発想です。

エージェントに作業させる場合、ここには固有の危険があります。ローカルやプレビューでの動作確認を頼んだつもりが、環境変数の解決順序の都合で本番へ接続していた、という事故です。マイグレーション実行と本番接続だけは、エージェントの許可リストから外す のが安全側の設計になります(Claude Code の権限設計)。

5. 観点3 — 公開範囲が意図通りか

AI を使った開発で最も事故が起きやすいのがここです。理由は単純で、作る速度が上がるほど「まだ見せるつもりのないもの」が本番相当の場所に増える からです。

プレビューURLは秘密ではない

Vercel のプレビューデプロイは、既定で公開 URL になります。URL を知っていれば認証なしでアクセスできます。ハッシュを含む長い URL は推測されにくいだけで、セキュリティ境界にはなりません。URL は次のような経路で本人の意図と無関係に流れます。

  • Slack や社内ツールに貼ったときの OG プレビュー取得
  • エラー監視・パフォーマンス計測サービスへの送信
  • CI のジョブログ、PR のコメント欄

そしてプレビューが見えるということは、そのビルドに含まれるクライアント公開の値も見える ということです。NEXT_PUBLIC_ 接頭辞の環境変数は設計上ブラウザへ露出するもので、プレビューが公開されていればプレビューからも読めます。検索エンジンにインデックスされる例も報告されています。

対処は Deployment Protection ですが、使える方式がプランで変わります

方式利用条件
Vercel Authentication全プランで利用可。Vercel チームメンバーへアクセスを限定する
Password ProtectionEnterprise、または Pro プランの有償アドオン
Trusted IPsEnterprise

つまり「パスワードをかけておく」という素朴な対処は、Hobby では選べず、Pro では追加費用が要ります。無料の範囲で選べるのは Vercel Authentication、つまり チームメンバーに限定する 方式です。外部の関係者にプレビューを見せたい要件がある場合、ここで設計が詰まります。各方式の挙動、保護範囲の指定、Hobby で本番ドメインを守れない制約は 管理画面を守る(Vercel) が詳しいので、本番前チェックではその記事で自分の契約プランに該当する行を確認してください。押さえるべきは、プレビューを閉じられるかどうかが契約プランに依存する という事実を、設計の前提として先に確認しておくことです。

3プラットフォームでの見方

確認事項CloudflareVercelSupabase
既定の公開範囲Worker のルートに割り当てた範囲で公開されるプレビューデプロイは既定で公開 URLRLS を有効にしていないテーブルは、公開鍵から実質公開
閉じる手段Cloudflare Access(アイデンティティ認識プロキシ)でホスト名への到達を ID 認証必須にできる。プレビュー環境保護に特化した手順は公式ドキュメントで要確認Deployment Protection(上表のプラン条件)テーブルごとに RLS を有効化し、ポリシーを書く
無料枠Access には無料枠のシート数がある。適用範囲は公式ドキュメントで要確認Vercel Authentication は利用可。Password Protection / Trusted IPs は上表のとおりRLS は全プランで利用可
確認方法認証されていないブラウザで到達を試すシークレットウィンドウでプレビュー URL を開く公開鍵だけで各テーブルへ読み取りリクエストを送る

Supabase の RLS は、この連載では認証・認可の各論として第8回で扱います(認証・認可のレビュー各論)。仕様そのものは RLS を理解する にあります。本番前チェックとしての問いは1つだけです。「公開しているスキーマの全テーブルについて、RLS が有効で、かつポリシーが1つ以上あるか」 です。RLS を有効にしてポリシーを書かなければ全拒否になるため、有効化だけを機械的に確認しても足りません。

この観点の確認方法として最も速いのは、認証情報を何も持たない状態で自分のアプリを触る ことです。シークレットウィンドウを開き、公開鍵だけを持った状態でエンドポイントを叩き、何が返るかを見ます。10分で終わり、設定画面を1時間眺めるより多くを教えてくれます。

6. 観点4 — 認証情報の種類を取り違えていないか

「クライアントのバンドルに管理者権限の鍵が混ざる」は古典的な事故です。AI にコードを書かせる場面では、この事故が起きやすくなります。サーバー側で動く例とクライアント側で動く例が同じ会話の中に現れ、片方の書き方がもう片方へ流れ込むことがあるためです。

Supabase はこの領域で API キーの体系を刷新中です。

位置づけ
anonsb_publishable_...クライアントへ配ってよい鍵。権限は RLS で決まる
service_rolesb_secret_...RLS を迂回する鍵。サーバー側だけで使う

移行に関して、実務で押さえるべき点は次のとおりです。

  • 旧キーは2026年末までに非推奨化が予定されています(正確な廃止日は公式の告知で確認してください)。新旧は並行して稼働できますが、旧キーは移行後にダッシュボードで明示的に無効化するまで有効なままです。無効化までを移行作業に含めてください。
  • 新体系では secret key をブラウザから使うと 401 が返ります(User-Agent ヘッダで判定)。
  • 新しいキーは apikey ヘッダ専用で、Authorization: Bearer には使えません。誤用は明示的な失敗として現れます。

ここが重要な点です。旧体系では、service_role キーをクライアントに置いても 動いてしまいました。動くので気づきません。新体系では同じ間違いが 401 という形で表面化します。沈黙する事故が、失敗する事故に変わった わけです。

この性質は本番前チェックの方法を変えます。旧体系のときは「バンドルを grep して鍵の文字列を探す」しかありませんでしたが、新体系では次のように問いを立て直せます。

# ビルド成果物に秘密鍵の接頭辞が混ざっていないかを機械的に確認する
grep -r "sb_secret_" ./dist ./.next 2>/dev/null && echo "混入あり: 直ちに鍵をローテーションする"

同じ考え方は他の基盤にも適用できます。「クライアントへ配ってよい鍵」と「配ってはいけない鍵」を接頭辞や命名で区別できる状態にしておき、ビルド成果物に対する検査を CI に置く のが本質です。命名で区別できない鍵は、検査もできません。混入が見つかった場合、その鍵はすでに配布されたものとして扱い、削除ではなくローテーションで対応します。

7. 観点5 — 外部からの流入をどこで絞るか

公開後に効いてくる観点です。総当たり、スクレイピング、大量リクエストによる従量課金の急増をどこで止めるかを、公開前に決めておきます。ここは 無料枠で何ができるかが3つとも大きく異なる ため、期待値を正確に持つことが重要です。

確認事項CloudflareVercelSupabase
WAFFree は Cloudflare Free Managed Ruleset のみが既定で有効(影響度の最も高い脆弱性のみカバー)。ルールのカスタマイズ、OWASP Core Ruleset、custom rules の大半は上位プランFirewall の基本機能(DDoS 緩和・IP ブロック・custom rules)は全プランで使えるが、ルール数の上限がプランで変わる。Managed Rules は上位プラン該当なし(前段の CDN / ホスティング側で行う)
ボット対策Bot Fight Mode は無料で利用可(基礎的な対策のみ)。機械学習ベースのスコアリングやカスタム allow/block は Enterprise 向け Bot Management アドオンBotID がある。Deep Analysis の無償提供は期間限定のプロモーションで、終了後の料金体系は公式ドキュメントで要確認該当なし
レート制限Free は基本エンジンで1ルール。Pro は2ルール(1分ウィンドウ)。Business は5ルール(10分ウィンドウ)+ regex 対応Rate Limit ルールは上位プラン。具体的な件数は Vercel 安全に使える領域マップ を参照該当なし(アプリケーション側で実装する)
ネットワーク制限Access / Tunnel を使った到達制御Trusted IPs(Enterprise)Network Restrictions で許可 IP 範囲を設定(対応プランは公式ドキュメントで要確認)。認証前の障壁として働く(DB 資格情報の検証より前段でブロックされる)
通信の保護既定で TLS既定で TLS既定では SSL / 非SSL の両方を許可(互換性優先)。非SSL接続の拒否を明示的に有効化できる

無料枠に関して率直に書くと、Cloudflare の Free で守れるのは「最も影響度の高い既知の脆弱性」と「基礎的なボット」までで、レート制限は1ルール です。ログインエンドポイントに1ルールを置けば、それで枠は尽きます。「Cloudflare の後ろにあるから大丈夫」とは言えません。1ルールをどこに置くかを、事前に決めておく必要があります。

Supabase の Network Restrictions が「認証前の障壁として働く」点は、実務上の価値が大きい特性です。資格情報の検証より前に接続が落ちるため、DB のパスワードを推測する試行そのものが到達しません。接続元が固定できる構成(サーバーからの接続のみ、など)なら、公開前に入れておく価値があります。ただし Network Restrictions を適用すると Edge Functions は PostgreSQL への直接接続を失うため、supabase-js 経由の接続へ切り替える必要があります。この副作用を知らずに適用すると、公開直前に Edge Functions が落ちます。

非SSL接続の拒否は、明示的に有効化するものです。既定は互換性優先で両方を許可しています。「暗号化されているはず」ではなく、設定として閉じたかどうかを確認してください。

8. 観点6 — 短命な認証情報に置き換えられるか

観点1で「秘密を安全に保管する」話をしましたが、根本的な対処は 保管すべき長命の秘密を減らす ことです。漏れても数分で失効する認証情報であれば、インシデント時の被害の窓が小さくなります。

Vercel の OIDC Federation は、この方向の代表的な仕組みです。GA 済みで、公式ドキュメントでは全プランで利用可能と案内されています。プラン条件は変わることがあるため、導入判断の前に公式ドキュメントと Vercel 安全に使える領域マップ の両方で確認してください。

  • ビルドおよび関数の呼び出しごとに、短命で RSA 署名されたトークンが発行される
  • AWS / GCP / Azure などがそのトークンを信頼し、短命な認証情報へ交換する
  • トークンの sub クレームに team / project / environment が符号化されるため、信頼する側で environment を条件に含められる

3点目が設計上重要です。preview から発行されたトークンでは production 用のロールを引き受けられないよう、クラウド側の信頼ポリシーで条件を書けます。つまり プレビューが本番の権限を継承しない構成を、鍵を配らずに作れます。観点2(環境分離)と観点1(秘密の保管)を同時に解く手段だと理解すると位置づけがはっきりします。

長命のアクセスキーを環境変数に置く構成と比べたときの差は、次のとおりです。

長命キーを環境変数に置くOIDC Federation
漏れたときの有効期間ローテーションするまでトークンの有効期間
ローテーション作業手動、全環境ぶん不要
環境ごとの権限分離環境ごとに別のキーを発行し管理するsub クレームの条件で表現する
保管すべき秘密あるクラウド側の信頼設定のみ

すべての外部連携を OIDC に置き換えられるわけではありません。対応していない外部サービスの API キーは長命のまま残ります。公開前の現実的な判断は、「短命化できるものはしたか」「残った長命の秘密は何と何か、一覧できているか」 の2問です。後者に答えられない状態で公開すると、第3章で見たようなローテーション要請を受けたときに動けません。

9. 公開直前チェックリスト

上記6観点を、そのまま実行できる形にまとめます。該当しない行は飛ばして構いませんが、飛ばした理由を1行書き残す ほうが後で役に立ちます。

観点1 — 秘密情報

  • リポジトリと履歴に鍵・トークン・接続文字列が含まれていない
  • Cloudflare: 機密は wrangler secret 側にあり、Wrangler 設定の vars に置いていない
  • Cloudflare: .dev.vars / .dev.vars.<environment-name>.gitignore に入っている
  • Vercel: 秘密にあたる環境変数すべてに Sensitive フラグが付いている
  • Vercel: Settings > Security & Privacy で新規変数の自動 Sensitive 化を有効にしてある
  • Supabase: DB 関数・トリガー・pg_cron・Edge Functions が使う秘密は Vault にある
  • 長命の秘密の一覧があり、ローテーション手順が書かれている

観点2 — 環境分離

  • 環境ごとに変数と接続先が分かれている
  • プレビュー環境から本番のデータストアへ接続していない(書き込みを1回試して確認した)
  • マイグレーションの実行と本番接続が、エージェントの自動実行の対象外になっている

観点3 — 公開範囲

  • シークレットウィンドウで、認証なしに見えるものを実際に確認した
  • Vercel: プレビューデプロイの保護方式を決めてある(自プランで選べる方式を確認済み)
  • Vercel: クライアント公開接頭辞の環境変数に、見せたくない値が入っていない
  • Supabase: 公開スキーマの全テーブルで RLS が有効で、ポリシーが1つ以上ある
  • Cloudflare: 保護したいホスト名の到達制御方針を決めてある(Access の具体的な構成は公式ドキュメントで確認する)

観点4 — 認証情報の種類

  • クライアントへ配る鍵とサーバー専用の鍵が、命名で区別できる
  • ビルド成果物に対する秘密鍵の混入検査が CI にある
  • Supabase: 新 API キー体系への移行方針が決まっており、移行済みなら旧キーを無効化した
  • 混入が見つかった鍵は、削除ではなくローテーションで対応した

観点5 — 流入制御

  • 認証エンドポイントに対するレート制限を、どこで行うか決めてある
  • 自プランで使えるルール数を確認したうえで、どこに配分するか決めてある
  • Supabase: Network Restrictions の要否を判断した(適用する場合、Edge Functions の接続方式への影響を確認した)
  • Supabase: 非SSL接続の拒否を有効にした
  • 従量課金のアラートを設定してある

観点6 — 短命な認証情報

  • Vercel: OIDC Federation で置き換えられる外部連携を洗い出した
  • preview から発行されたトークンで production の権限を得られない構成になっている
  • 残った長命の秘密が一覧化されている

まとめ

  • プラットフォームは「安全にできる」だけで、既定で安全ではありません。Sensitive フラグ、RLS、非SSL拒否、Deployment Protection は、いずれも明示的に有効化するものです。
  • 2026年4月の Vercel インシデントでは、Sensitive フラグの無い環境変数が列挙・復号されました。マネージドに預けたことは、フラグを立てなかった秘密を守りません。
  • プレビュー URL は秘密ではありません。そして閉じられるかどうかが契約プランに依存するため、設計の前提として先に確認する必要があります。
  • Supabase の新 API キー体系では、管理者用の鍵をブラウザから使うと 401 で失敗します。沈黙していた事故が表面化するようになりました。
  • 無料枠でできることには明確な上限があります。Cloudflare Free のレート制限は1ルールです。「前段にあるから大丈夫」ではなく、1ルールをどこに置くかを決めてください。
  • 公開前の点検はプラットフォームごとに行うと抜けます。6つの観点を縦軸にして、3列を横に並べて見てください。

次回は、ここで一覧化した秘密情報そのものの管理と、依存パッケージ側から入ってくるリスクを扱います。

参考リンク