AIはAPIキーを、まずフロントに書く — フロントに書いたものは全員に見える

AIツールにアプリを作らせると、APIキーはまずフロントエンドのコードに直接書かれます。動作確認が早いからです。でも、ブラウザに届いた時点で誰でも読める。F12での確かめ方、サーバー側へ移す頼み方、そして「移すだけでは足りない、再発行する」まで。

動画で見る

結論から

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分で終わります。

  1. デプロイ済みの自分のアプリをブラウザで開く(ローカルではなく、公開URL)
  2. F12(Mac は Cmd + Option + I)で開発者ツールを開き、Sources タブへ
  3. Cmd/Ctrl + Shift + F で全ファイル検索を開き、sk- service_role API_KEY SECRET で検索
  4. ヒットしたら、そのキーは世界中に公開されている

デモ用に用意した予約アプリでは、/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本番化の診断で一緒に確認することもできます。