GCPの料金アラートで冷や汗をかいた日。Firebase APIキー直撃の不正アクセスとApp Checkの罠

最近はすっかり暖かくなってきて、ギターを触るのにも指がスムーズに動いて良い季節になりました。しかし、技術の世界では、温かい風どころか冷たい汗が吹き出すような事件が起きるものです。インフラ周りを甘く見ていると、ある朝突然、心臓に悪いイベントが発生します。

今回は、私が運用しているタロット占いアプリで、GCP(Google Cloud Platform)の料金アラートから発覚した不正アクセスの一部始終と、その対策についてメモしておきます。

1. 朝一番のGCP料金アラートで飛び起きる

ある日の朝、スマホの通知音で目が覚めました。画面を見ると、GCPからのアラートメール。

普段なら無料枠か、せいぜい数十円程度で収まっているはずのFirebaseプロジェクトなのですが、メールには「予測請求額が設定された予算のしきい値を超えました」との文字が。あわてて管理画面を覗いてみると、数千円規模の請求が発生しているではありませんか。へーー、と呑気に感心している余裕はありません。一瞬で目が覚めました。

2. とりあえず応急処置をして、お仕事へ

原因を突き止めたいのは山々ですが、日中は本業の仕事があります。このまま課金が膨らみ続けるのを見過ごすわけにはいかないので、とりあえずの応急処置として、予算アラートの設定を引き上げつつ、一時的にAPIの利用上限を絞ったり、不審なリクエストが来ていないか部分的にシャットアウトする設定を行いました。

「大丈夫だろうか……」と終始もやもやした不安を抱えたまま、なんとか日中の仕事をこなしました。仕事中も頭の片隅でずっとGCPのダッシュボードがチラついていました。

3. 帰宅後、AI(Gemini)とログ解析をして確信へ

帰宅後、さっそく本格的な原因調査を開始しました。ログの海から自力で原因を探すのは骨が折れるので、GCPのログをエクスポートし、Geminiに解析を依頼しました。最初は「何か自分が書いたコードが無限ループでも起こしたのかな?」と思っていたのですが、AIとログを検証するうちに、冷酷な事実が浮かび上がってきました。

「これは不正アクセスです」

4. ログ解析で分かった驚くべき「直撃ルート」

さらにログを詳細に解析してもらうと、奇妙なデータが見つかりました。

  • Vertex AIを呼び出すための正規のバックエンド関数(Cloud Functionsの onReading)の実行回数:同期間でわずか9回
  • Vertex AIの請求金額:8,832 JPY(大量のテキスト/画像推論リクエストが計上されている)

バックエンドが9回しか動いていないのに、Gemini API(Vertex AI)の請求が8,000円以上になっている。つまり、正規のWebアプリ画面を経由せず、APIが直接叩かれていることを示しています。

さらにFirebase Hostingのアクセスログを調べると、フランスのIPアドレス(185.177.72.11185.177.72.38)から、自動スクリプト(User Agent: curl/8.7.1 など)による脆弱性スキャンや、APIエンドポイントへの連続リクエストが検出されました。

# Hostingログに残された不審なアクセスエビデンス
2026-07-03 12:05:18 | POST | https://kui-free-tarot.web.app/vendor/.../eval-stdin.php | 185.177.72.38 | curl/8.7.1

真の侵入ルート:APIキーの直撃とApp Check適用設定の漏れ

攻撃の全貌はこうでした。

  1. 攻撃者は、ブラウザに読み込まれるフロントエンドのJSファイル(index-CYSbJOoC.js等)から、Firebaseの接続設定(apiKeyprojectId)を抽出。
  2. 抽出したAPIキー(Browser key)を使い、自分のローカルスクリプト等から、Googleの Vertex AI エンドポイントへ直接リクエストを送信(APIキーの直撃)。
  3. このとき、Firebase App CheckでVertex AI APIに対する「強制適用(Enforced)」の設定が行われておらず、たんなる「監視(Monitoring)」のままになっていたため、有効なアプリトークンを持たない直接リクエストがすべて通過してしまった。

APIキー自体は公開される前提のものですが、そこに紐づくAPI制限とApp Checkの強制化が漏れていたのが致命傷でした。

5. セキュリティ設定の修正対応

原因がわかれば、あとは穴を塞ぐだけです。以下の対策を即座に実施しました。

① GCPコンソールでのAPIキー制限(最優先)

GCPの「APIとサービス」>「認証情報」から、公開している Browser key などの設定を開き、「API制限」をかけました。Webアプリがクライアントから直接Geminiを叩く必要がない場合は、許可APIリストから Firebase Vertex AI API (firebasevertexai.googleapis.com) を完全に除外(チェックオフ)します。

② Firebase App Check の強制適用(Enforce)

Firebaseコンソールの「App Check」>「API」タブから、Vertex AI を認証対象に追加し、ステータスを「適用(Enforce)」に変更しました。これにより、正規のアプリ(今回の場合は KuiTarot/32 などのUser Agentを持つもの)から発行された有効なApp Checkトークンを持たないリクエストは、APIキーが正しくてもGoogleのインフラ側で自動的に遮断されます。

③ APIのクォータ(上限)制限

GCPの「割当(Quotas)」から、Vertex AI APIの1分あたり・1日あたりの最大リクエスト数を、個人開発の運用で現実的な最小値(例:1分あたり10回など)に制限しました。万が一キーが再度漏洩しても、請求が破綻するのを防ぐためです。

6. 対策後も増え続ける「請求金額」の恐怖、そして結末

設定をすべて修正し、「よし、これで不正アクセスは完全に防げるはず!」と一安心したのですが、そこからまた別の恐怖が待っていました。

対策を終えた後も、GCPの課金ダッシュボードの請求金額が、1時間ごとにジワジワと増え続けていくのです。「えっ、まだどこかから漏れてる!?設定が間違っていたのか!?」とパニックになりました。

しかし結論から言うと、これはGCPの課金データ反映のラグ(数時間〜半日の遅延)によるものでした。実際にはアクセスは遮断できているのですが、過去に発生した不正アクセスの請求データが遅れてグラフに反映されていたため、増えているように見えただけでした。これは精神衛生上、本当に心臓に悪かったです。

ちなみに、最終的に確定した請求額の約8,800円ですが、結論から言うと実害はありませんでした!

運が良いことに、プロジェクトに設定していた「GCPのAI用無料枠クレジット」の残高の範囲内に綺麗に収まってくれたため、全額クレジットから相殺され、私の身銭を切る支払い(実費持ち出し)は「0円」で済みました。本当に胸をなでおろしました。

7. Firebaseのデフォルト設定は「超ゆるい」ので気をつけよう

今回はクレジットのおかげで命拾いしましたが、もし無料枠がなければ普通に数千円(リクエスト数がさらに多ければ数万〜数十万円)の請求が来ていたわけで、本当に背筋が凍る思いでした。

今回の教訓は、「Firebaseが自動生成するデフォルトのAPIキーは最初から制限がゆるい」という点に尽きます。

Firebaseプロジェクトを作成した初期状態では、Browser keyなどはAPI制限が一切かかっておらず、そのプロジェクト内のあらゆるGoogle Cloud APIを呼び出せてしまう設定になっています。APIキーを公開する場合は、必ず以下の設定をプロジェクト初期に実施する癖をつけましょう。

  • 使用するAPIキーには、必要最小限のAPI(AuthやFirestoreなど)だけを許可し、不要なAPIはチェックを外す。
  • App Checkを導入したら、監視(Monitor)のまま放置せず、必ず「強制適用(Enforce)」にする。
  • 課金上限(Quotas)やリアルタイムの予算アラートは、プロジェクト立ち上げ時にセットしておく。

みなさんも、ある日突然の料金アラートで冷や汗をかかないよう、APIキーの制限とApp Checkの設定は見直しておきましょう。

めでたし。

Follow me!