Qiitaトレンドで回ってきた「Google APIキー漏洩で13時間で約900万円請求」の記事、正直心臓が止まりかけた。新しい話ではない。新しくないから怖い。
私はAI生成コード特化のセキュリティスキャナー CodeHeal を個人開発していて、この手の「APIキー1本で数十万〜数百万円が吹き飛ぶ」パターンを毎週のように見ている。今回の事案は、スキャナー開発の動機そのものがそのまま現実に起きた例なので、当事者視点でまとめておきたい。
何が起きたか(要点)
- Firebase + Gemini 構成の個人開発アプリ
- Google API キーがフロントエンドバンドルに露出
- Application 制限なし / API 制限なし
- スクレイパーがキーを拾い、Geminiを叩きまくって13時間で約900万円
- 請求が来るまで気づかない
「認証突破された」でも「ゼロデイ脆弱性を踏んだ」でもない。キーを制限せず置いておいただけ。それで終わる。
元記事: 【こわい】Google APIキーの脆弱性により13時間で約900万円請求される事案が発生
なぜAI生成コード時代に悪化したか
CodeHeal を作り始めた頃、GitHubから「Firebase + AI スターター」系のpublicリポジトリを40本ほど落としてスキャンにかけた。32本が、キー直書きまたは制限なしFirebase configをプレーンテキストで持っていた。チェリーピックではない。検索結果の上位2ページから持ってきただけ。
理由は3つある。
1. LLMは「とりあえず動く」を最短距離で出す
「Next.js に Firebase + Gemini を繋いで」とLLMに頼むと、一番短い答えはクライアント側にconfigオブジェクトを置く書き方になる。動く。デモが通る。デプロイされる。LLMは「このキー、ブラウザに出ますけど制限かけました?」と聞いてくれない。
2. Firebase公式ドキュメントが紛らわしい
Firebase web config は「公開してもよい」と書かれている。ただしそれはApplication制限 + Firebase Security Rules がセットの前提。Geminiなど別APIの権限が同じキーに付いた瞬間、その前提は崩れる。
3. フィードバックループが壊れている
キーが漏れたと知るのは請求書が来たとき。CI も型チェックもテストも通る。ここは静的解析が最も効く領域。
実は私も最初LLMでスキャナーを作ろうとして失敗した
最初のCodeHealプロトタイプはLLMベースだった。同じリポジトリを5回スキャンさせたら5回違う結果が返ってきた。直書きキーを見落としたり、存在しないJWT漏洩を幻覚したり、process.env.NEXT_PUBLIC_API_KEY を「env vars使ってるから安全」と判定したり。最後のやつが致命的で、NEXT_PUBLIC_* はクライアントに出るのが今回の900万円事案そのものの構造。
その日にLLMを引っこ抜いて、AST解析 + パターンマッチ + ルールエンジンに書き直した。同じコードには同じ結果が返る。セキュリティツールでは再現性100%が前提。市場にある「AI security」系スキャナーの多くがLLM-in-the-loopで動いているのは、正直怖いと思う。
現時点のCodeHealは 14カテゴリ・93ルール。ハードコード秘密情報・未制限クレデンシャルのカテゴリは、実運用で一番ヒットする。
今日のうちに10分でやること
- Google Cloud Console → APIs & Services → 認証情報 を開く。全APIキーに アプリケーション制限(HTTPリファラ / IP / アプリ)と API制限 の両方を設定する。片方じゃダメ。両方。
-
ビルド後のJSを開いて
AIzaで検索(Google APIキーのプレフィックス)。制限なしキーを見つけたら今すぐローテート。朝会の後じゃなく今。 - 予算アラート必須。Googleのデフォルトは「上限なし」。
- Gemini / Vertex キーは絶対にクライアントに置かない。バックエンド経由でプロキシ。プロトタイプでも例外なし。
- push前にスキャナーを回す。pushの後じゃない。前。
CodeHealはこのカテゴリをどう扱うか
検出ルールの正規表現とスコアロジックは商品の核なので公開しないが、設計思想は書いておく。
- クライアント側に出るソースファイル(Next.js
NEXT_PUBLIC_*、ViteVITE_*、CRAREACT_APP_*、およびrawな文字列パターン)を対象にキー様リテラルを探す - プロバイダ別プレフィックス(Google / OpenAI / Anthropic / Stripe など)と照合し、信頼度レベルを分ける
- フレームワーク固有の罠 — Firebase config オブジェクト、Vercel envの直書きコミット、誤って追跡された
.env.local— を個別ルールで見る - 検出結果は件数ではなく**blast radius(影響範囲)**でランク付け。漏れたGeminiキー1本 > 無害なwarning 100件
Firebase + Gemini の典型構成に対して、スキャンは2秒以内に終わり、未制限キーをCritical判定で出す。今回の事案の開発者がもし最初のpush前にCodeHealを走らせていたら、請求書より先に警告が出ていた。
不都合な真実
AIは1週間の仕事を1日で終わらせてくれる。同時に、1週間かかる事故も1日で起こせるようにした。加速は対称。Google / OpenAI / Anthropic の課金システムはあなたを守らない。使用量こそ収益なので守らないように作られている。
静的解析は、AIでノリで書いたアプリに対する一番安い保険になる。
まとめ
| 項目 | 内容 |
|---|---|
| 事案 | 約900万円請求 / 13時間 / Firebase + Gemini 漏洩キー |
| 根本原因 | 未制限APIキーがクライアント露出 |
| AI時代の悪化要因 | LLMは「動く」を優先、秘密情報衛生は後回し |
| 10分で出来る対処 | アプリ制限 + API制限、課金上限、Geminiはバックエンド経由 |
| 根本対処 | push前の静的スキャンをCIに組み込む |
自分のAI生成コードに同じ地雷がないか不安な方は、ブラウザ上で無料スキャンできます(無料プランは1日5回、サインアップ不要)。
14カテゴリ・93ルール・LLM不使用。同じコードには常に同じ結果を返します。スクレイパーより先に自分で気づけるように。