IdeaValue

APIキー漏えいの確認方法と対処:NEXT_PUBLIC_・VITE_で公開される環境変数に注意

執筆:IDEAVALU運営 · 更新:

ブラウザに届いた JavaScript は、誰でも開発者ツールで読めます。秘密のAPIキーが含まれていれば、第三者があなたの名前でAPIを呼び出し、利用料を請求されたりデータを消されたりします。AIに書かせたコードでは、キーをブラウザ側に直接書いてしまう例が特に多い項目です。

なぜ秘密のキーが公開されてしまうのか

もっとも多いのは、環境変数の名前によってビルド時に JS へ埋め込まれるケースです。Next.js は NEXT_PUBLIC_、Vite は VITE_、Create React App は REACT_APP_ で始まる変数を、ブラウザで使えるように JS の中へ値ごと書き込みます。秘密のキーにこの接頭辞を付けると、デプロイした時点で公開されます。

もう一つは、OpenAI や Stripe の API をブラウザから直接呼ぶコードです。動かすためにキーをブラウザへ渡す必要があるので、キーも一緒に配られます。秘密のキーを使う処理は、API Route・Server Action・Edge Function などのサーバー側に置き、ブラウザはそのサーバーを呼ぶ形にします。

公開してよいキーと、いけないキー

サービスによっては、ブラウザに置く前提で作られたキーがあります。見分け方の例です。

  • 公開してよい:Supabase の anon キー・publishable キー(sb_publishable_)、Firebase の Web 用 apiKey、Stripe の公開キー(pk_live_)。ただし、データを守る設定(Supabase の RLS、Firebase のセキュリティルール)が前提です。
  • 公開してはいけない:Supabase の service_role キー・シークレットキー(sb_secret_)、Stripe のシークレットキー(sk_live_)・制限付きキー(rk_live_)、OpenAI・Anthropic の APIキー、AWS のアクセスキー、GitHub のトークン、Slack・Discord の Webhook URL、メール配信サービスの APIキー。
  • Google Maps などの APIキーは公開して使うものですが、管理画面で使えるドメイン(HTTP リファラー)を制限してください。

自分で確認する方法

ビルドした成果物の中を検索すると、デプロイ前に気づけます。ブラウザの開発者ツールの「Sources」で全ファイル検索(Windows は Ctrl+Shift+F、Mac は Cmd+Option+F)をしても確認できます。

Next.js のビルド成果物を検索する

npm run build
grep -rEo "sk_live_[0-9A-Za-z]{8}|sk-(proj-)?[A-Za-z0-9_-]{8}|sb_secret_|service_role" .next/static | sort -u

Vite のビルド成果物を検索する

npm run build
grep -rEo "sk_live_[0-9A-Za-z]{8}|sk-(proj-)?[A-Za-z0-9_-]{8}|sb_secret_|service_role" dist | sort -u

再発を防ぐ

Next.js では、秘密のキーを読むファイルの先頭に import 'server-only' を書くと、そのファイルを誤ってブラウザ側から読み込んだときにビルドが失敗します。GitHub の secret scanning(プッシュ保護)を有効にすると、キーを含むコミットを push する前に止められます。

秘密のキーを使うファイル(Next.js)

import 'server-only'

export async function createCompletion(prompt: string) {
  // process.env.OPENAI_API_KEY は NEXT_PUBLIC_ を付けない
  const apiKey = process.env.OPENAI_API_KEY
  // ...
}

直し方

公開JSに秘密鍵が含まれていないか

ブラウザに届いた鍵は、すでに誰でも読める状態です。消すだけでなく、必ず鍵を無効化して作り直してください。

1. 各サービスの管理画面で、該当する鍵を今すぐ無効化(revoke / roll)する。

2. 新しい鍵はサーバー側(API Route・Server Action・Edge Function)だけで使い、ブラウザからはそのサーバーを呼ぶ形にする。

3. 環境変数名を確認する。Next.js の NEXT_PUBLIC_、Vite の VITE_、Create React App の REACT_APP_ で始まる変数は、ビルド時に JS に埋め込まれて公開されます。秘密の値にはこの接頭辞を付けないでください。

4. Supabase の service_role キーが漏れていた場合は、RLS をすべて素通りできる状態でした。キーを再発行し、データが書き換えられていないか確認してください。

5. 鍵を Git に commit していた場合は、履歴にも残っています。鍵の無効化を優先し、公開リポジトリなら履歴の書き換えも検討してください。

よくある質問

Supabase の anon キーがJSに入っているのは問題ですか?
anon キーと publishable キーはブラウザに置く前提の値なので、それ自体は問題ありません。その代わり、RLS で他人のデータを読めない・書けないようにしておく必要があります。service_role キーやシークレットキーが入っている場合は、すぐに無効化してください。
キーを消して再デプロイすれば大丈夫ですか?
大丈夫ではありません。公開されていた間に誰かがコピーしているかもしれないので、必ず各サービスの管理画面でキーを無効化して作り直してください。Git の履歴や、CDN にキャッシュされた古い JS にも残っています。
APIキー漏えいの確認方法と対処|NEXT_PUBLIC_・VITE_の落とし穴 | IDEAVALU