結論から
AIツールにアプリを作らせると、APIキー(OpenAI・Supabase・Stripe などの合言葉)は、まずフロントエンドのコードに直接書かれます。 理由は単純で、そのほうが動作確認が早いからです。
この記事で伝えたいのはこの一点です。フロントエンドに書いたものは、ブラウザに届いた時点で誰でも読める。キーはサーバー側の環境変数に移し、いちど書いてしまったキーは再発行する。
- ブラウザで
F12→Sourcesを開き、sk-やservice_roleで検索すると、そのまま出てくる - 見つけた人は、あなたの名義でAPIを使える。請求はあなたに来る
- 移す先は「サーバー側」。画面はサーバーに頼み、サーバーがキーを使う
- 移すだけでは足りない。古いキーは無効化して、新しく発行する
動画版(約2分)は本文の上に埋め込んであります。
① AIはこう書く
AIツールに「OpenAIで文章を要約する機能を付けて」と頼むと、最初に出てくるコードは、だいたいこの形です。
const OPENAI_API_KEY = "sk-proj-9f3K…";
fetch("https://api.openai.com/v1/chat/completions", {
headers: { Authorization: "Bearer " + OPENAI_API_KEY },
});画面側(ブラウザで動くJavaScript)から、キーを付けて直接 OpenAI を呼んでいます。動きます。しかも一発で動くので、AIも人も「できた」と思ってしまいます。
Supabase でも同じことが起きます。anon キーは公開前提なので画面に置いてよいのですが、AIは「管理用の service_role キーのほうが権限の制限がなくて楽」という理由で、そちらを画面側に書くことがあります。こちらは 絶対に ブラウザに置いてはいけないキーです。
② なぜ危ないのか:ブラウザに届いた時点で、全員に見える
フロントエンドのコードは、あなたのアプリを開いた人全員のブラウザにそのまま送られます。「画面に表示していない」と「隠せている」は別です。確かめ方は1分で終わります。
- デプロイ済みの自分のアプリをブラウザで開く(ローカルではなく、公開URL)
F12(Mac はCmd + Option + I)で開発者ツールを開き、SourcesタブへCmd/Ctrl + Shift + Fで全ファイル検索を開き、sk-service_roleAPI_KEYSECRETで検索- ヒットしたら、そのキーは世界中に公開されている
デモ用に用意した予約アプリでは、/app.js を開いただけで sk-proj-… と service_role の2つが並んでいました。特別なツールも知識も要りません。
見つけた人にできることは、「あなたの名義でそのAPIを使う」ことです。OpenAI のキーなら、あなたの支払い方法で大量に文章を生成させられます。Supabase の service_role なら、データベースの全テーブルを読み書きできます。アプリは普段どおり動き続けるので、気づくきっかけは「請求額の急増」か「サービスからの不審利用の通知」。つまり、被害が出てから気づく のが普通です。
規模感を1つだけ。GitGuardian の2026年版レポートによると、2025年に公開 GitHub へ新たに流出したシークレット(キーやパスワード)は 2,865万件、前年比 +34%。そのうちAIサービス(OpenAI など)のキーは 約128万件 で、前年比 +81% と最も急増しているカテゴリーです。フロントへの直書きと GitHub への公開は、キーが漏れる二大経路です。
③ なぜAIはそう書くのか
AIツールは「動くものを最短で出す」ことに最適化されています。フロントから直接 API を呼ぶ書き方は、サーバー側のコードを1つも書かずに動作確認ができる、いちばん短い道です。ここで「この値は誰に見えてよいか」を考える工程は、頼まれない限り入りません。
これはAIが不注意なのではなく、「動く」と「安全」が別の確認だから です(→ 「動く」と「安全」はなぜ別の話なのか)。AIは前者を高い精度で満たします。後者は、あなたが一言足す必要があります。
④ 直し方:サーバー側へ移して、キーを再発行する
やることは2つです。移す と 再発行する。移すだけで安心してしまう人が多いのですが、いちど公開されたキーは、履歴やキャッシュにコピーが残っている前提で扱います。
1. サーバー側へ移す。 以下をそのまま AI ツールに貼ってください。
「このプロジェクトで、ブラウザに配信されるフロントエンドのコードに含まれている APIキー・シークレット(OpenAI、Supabase の service_role、Stripe の secret key など)をすべて列挙してください。それぞれについて、キーをサーバー側の環境変数(.env と本番のホスティング設定)に移し、フロントエンドはサーバー側のエンドポイント経由で呼び出すように書き換えてください。Supabase の anon キーのように公開してよいものは、その理由を添えて残してください。変更したファイルの一覧を最後に出してください。」
2. 古いキーを無効化して、新しく発行する。 OpenAI なら Platform の API keys 画面で該当キーを Revoke、Supabase なら Project Settings → API で service_role を Regenerate。新しいキーは環境変数にだけ置きます(→ 環境変数とは何か)。
⑤ 確かめる:もう一度、検索する
直したあとに、②の手順をもう一度やってください。デプロイ後の公開URLで F12 → Sources → 全文検索 をして、sk- service_role がヒットしなければ合格です。ローカルで確認して満足しないこと。本番のビルドに古い値が残っていることは珍しくありません。
あわせて、GitHub にコードを上げている人は、リポジトリ内も同じ語で検索してください。過去のコミットに残っている場合は、履歴を消すより 再発行のほうが確実 です。
よくある質問
Supabase の anon キーが画面に見えています。これも隠すべきですか。
anon キーは「公開してよい前提」で設計されたキーなので、見えていること自体は問題ありません。ただし、それが安全なのは RLS(行レベルセキュリティ)が有効なとき に限ります。RLS が無効なら、anon キーだけで全テーブルが読めます。次の記事(Moltbook の事例)で、まさにこれが起きています。
環境変数に移したのに、まだブラウザで見えます。
NEXT_PUBLIC_ や VITE_ で始まる環境変数は、名前のとおり ブラウザに配信される 設定です。秘密のキーにこの接頭辞を付けてはいけません。AIツールは「環境変数に移して」と言うと、この接頭辞を付けて "移した" ことにする場合があります。移した先が本当にサーバー側かどうかは、⑤の手順で確かめます。
個人の趣味アプリで、キーの無料枠しか使っていません。それでも危険ですか。
無料枠でも、他人が使い切ればあなたのアプリが止まります。また、無料枠の悪用が続くとアカウントごと停止されることがあります。従量課金に切り替えた瞬間に請求が来るタイプのキーもあります。「金額が小さい」ではなく「他人があなたの名義で使える」ことが問題です。
自分で直せません。エンジニアに何と伝えればよいですか。
「フロントエンドのコードに OpenAI(など)のキーが直書きされています。サーバー側の環境変数へ移して、フロントからはサーバー経由で呼ぶように変更してください。古いキーは再発行済みです/再発行もお願いします」。これで作業範囲が正確に伝わります。自分でどこまでやるか迷う場合は、VibeCode本番化の診断で一緒に確認することもできます。


