30
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

13時間で900万円請求事件の裏側 - AI生成コードが招くAPIキー漏洩を静的解析で止める

30
Last updated at Posted at 2026-04-18

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分でやること

  1. Google Cloud Console → APIs & Services → 認証情報 を開く。全APIキーに アプリケーション制限(HTTPリファラ / IP / アプリ)と API制限両方を設定する。片方じゃダメ。両方。
  2. ビルド後のJSを開いて AIza で検索(Google APIキーのプレフィックス)。制限なしキーを見つけたら今すぐローテート。朝会の後じゃなく今。
  3. 予算アラート必須。Googleのデフォルトは「上限なし」。
  4. Gemini / Vertex キーは絶対にクライアントに置かない。バックエンド経由でプロキシ。プロトタイプでも例外なし。
  5. push前にスキャナーを回す。pushの後じゃない。前。

CodeHealはこのカテゴリをどう扱うか

検出ルールの正規表現とスコアロジックは商品の核なので公開しないが、設計思想は書いておく。

  • クライアント側に出るソースファイル(Next.js NEXT_PUBLIC_*、Vite VITE_*、CRA REACT_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回、サインアップ不要)。

👉 CodeHealで無料スキャンしてみる

14カテゴリ・93ルール・LLM不使用。同じコードには常に同じ結果を返します。スクレイパーより先に自分で気づけるように。

30
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?