クライアントサイドのJavaScriptに露出したAPIキー
ブラウザに送られるもの——インラインスクリプト、バンドルされたJS、source map——は誰でも読めます。フロントエンドのコードに埋め込まれたシークレットキー(OpenAI、Stripeのシークレット、AWS、データベースURL)は、実質的に公開されているのと同じです。
これは何か
ビルドツールは、コンパイル時に環境変数をインライン展開します。クライアントのコードでシークレットを参照すると、それはすべての訪問者がダウンロードするJavaScriptバンドルに含まれてしまいます。
重要な区別があります。publishable/anon キー(Stripeの pk_live、Firebaseのweb設定)は公開されることを前提に設計されていますが、シークレットキー(sk_live、service-role キー、プロバイダーのAPIキー)はそうではなく、決してブラウザに届いてはいけません。
なぜ重要か
漏洩したプロバイダーのキーは、誰にでもあなたのアカウントで利用量を積み上げさせ——LLMの請求、SMSの料金、クラウドの費用——あるいはそのキーが許可するデータにアクセスさせてしまいます。
攻撃者は大規模にJavaScriptバンドルをスクレイピングし、キーのパターンを探しています。コミットされたシークレットは、公開後たいてい数分以内に悪用されます。
直し方
サードパーティのAPIは、ブラウザからではなく、サーバーのルートやバックエンドから呼び出してください。ブラウザはあなたのエンドポイントと通信し、キーはエンドポイントが保持します。
// app/api/summarize/route.ts (server-only)
export async function POST(req: Request) {
const { text } = await req.json();
const r = await fetch("https://api.openai.com/v1/responses", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}` },
body: JSON.stringify({ model: "gpt-4o-mini", input: text }),
});
return Response.json(await r.json());
}Next.jsでは、NEXT_PUBLIC_ のプレフィックスが付いた変数だけがブラウザに露出します。このプレフィックスの後ろにシークレットを置かないでください。公開しても安全な値にのみ使いましょう。
シークレットが一度ブラウザに配信されたら、失効させて再発行してください。次のビルドから削除しても、すでにスクレイピングされたコピーは無効になりません。
FAQ
Stripeの publishable キー(pk_live)はフロントエンドにあると問題ですか?
いいえ。publishable キーは公開される前提のものです。リスクはシークレットキー(sk_live)で、これはサーバーサイドにとどめる必要があります。
キーを難読化するだけではだめですか?
だめです。難読化は簡単に元に戻せます。唯一の対策は、シークレットをクライアントから完全に排除することです。