「Firebaseで作ったアプリのセキュリティ、本当に今のままで大丈夫?」 「もしbotにAPIを乱発されて、来月とんでもない請求が届いたらどうしよう……」
個人開発者から企業プロジェクトまで、Firebaseを使った開発で常に頭を悩ませるのが「セキュリティ対策」と「予期せぬ従量課金のバースト(破産アタック)」です。
セキュリティルールをどれだけ強固に書いても、悪意あるbotによるAPIの直接叩きや、登録フォームのスパム連打を完全に防ぐことはできません。そこで今、Firebase公式が導入を強く推奨している強力な盾が「Firebase App Check」です。
本記事では、App Checkの基本的な仕組みから、賢く運用するための「reCAPTCHA v3」との組み合わせ方、気になるコスト(料金)のリアルな話、そして導入時に必ずハマる注意点までを網羅しています。
さらに、Next.jsアプリをAppCheckを導入する具体的な導入手順をステップバイステップで徹底解説しています。
この記事を読めば、あなたのアプリをbotの脅威から守り、安心して本番公開を迎えるための「鉄壁の防御網」が手に入ります。
Firebase App Checkとは何か?
Firebase App Check(アップチェック)は、あなたのFirebase(データベースや関数など)に、許可されたアプリやWEBサイト以外からのアクセスを一切禁止するセキュリティ機能です。
なぜApp Checkが必要なのか?(鍵のコピー問題)
通常、WebサイトでFirebaseを使うとき、以下のような「設定値(Config)」をコードに書きます。
const firebaseConfig = {
apiKey: "AIzaSyA1...",
authDomain: "your-app.firebaseapp.com",
projectId: "your-app",
// ...
};
実は、この設定値はブラウザの「ソースコードを表示」を見れば、誰でも簡単に見ることができてしまいます。(これはFirebaseの仕様であり、セキュリティ上の欠陥ではありません)。
ここで問題が発生します。 悪意のあるプログラマー(ハッカー)が、あなたのWebサイトからこの設定値をコピーして、自分のPCのターミナルなどから、あなたのFirestoreに直接データを大量に送りつけるプログラムを実行できてしまいます。
これを防ぐのが App Check です。
App Checkの仕組み
App Checkを導入すると、Firebaseの前に「セキュリティゲート」が設置されます。
- 合言葉の確認
ユーザーが「あなたの本物のWebサイト」を開くと、Webサイトが自動的に「私は本物のサイトです」という証明書を提出します。 - トークンの発行
証明書が正しいと確認されると、Firebaseから「有効期限付きのApp Checkトークン」がWebサイトに渡されます。 - トークンの利用
WebサイトがFirestoreからデータを読み書きする際、このトークンを一緒に提示します。 - 不正アクセスの遮断
もしハッカーが設定値だけを盗んで直接アクセスしてきても、トークンを持っていないため、Firebaseの手前でシャットアウトされます。
裏でreCAPTCHA v3を使用している
App Check自体は「入場を検証するゲート」の役割をしますが、「本当に本物のWebサイトか?」を判定する目(証明プロバイダ)は、Webサイト(Next.jsなど)の場合、主に Googleの reCAPTCHA v3 が使われています。
- reCAPTCHA v3 の役割
ユーザーがサイト内で普通に操作している動きをAIが分析し、「これはbot(ロボット)じゃなくて人間の動きだな」「怪しいプログラムじゃないな」と判定します。 - ユーザーへの負担はゼロ
v3の場合、昔のような「信号機の画像を選んでください」という面倒なパズルは画面に出ません。バックグラウンドで完全に自動で判定されます。
App CheckのreCAPTCHA v3が検証するのは、Firestore、Cloud Functions、Firebase Authなどの「FirebaseのAPI」です。
外部の郵便番号検索APIのような、「Firebaseとは関係のない外部APIの通信」は対象外なので、別途セキュリティ対策を実装する必要があります。
その場合は、App Checkとは別に、画面のJavaScript側で単体のreCAPTCHA v3またはv2を実装し、スコアを検証するロジックを組む必要があります。
App Checkを入れる「最大のメリット」
最大のメリットは、「悪意のある大量アクセスによる、高額な課金(破産リスク)を防げる」ことです。
Firebaseは使った分だけお金がかかる「従量課金制」です。
もしbotによってデータベースに数百万回の書き込みをされたり、決済連携のCloud Functionsを何万回も実行されたりすると、翌月に見たこともないような高額な請求が届く危険があります。
App Checkを入れておけば、あなたのWebサイト「以外」からのリクエストはFirebaseに到達すらしない(ノーカウントになる)ため、お財布を強力に守ることができます。
App Checkは自体は無料(reCAPTCHAの無料枠も強力!)
Firebase App Check自体の利用料金は「完全無料」です。
ただし、App Checkが裏側で利用するエンジンの料金(今回のケースでは Google reCAPTCHA v3 の料金)がそのまま適用されます。
Next.jsなどのWebアプリで導入する場合、選択肢となるプロバイダは以下の2つです。プランに応じてコストが変わります。
1万回までは無料
個人開発や一般的なスタートアップ、新規サービスであれば、ほぼ確実に「完全無料」で運用できます。
- コスト: 月間 1万回 のリクエストまで無料
- 無料枠を超えた場合: 月間1万回(1日に約330回アクセス)を超えると、App Check(reCAPTCHA)が一時的にリクエストを正しく処理できなくなる、または有料プラン(Enterprise)への移行案内が来ます。最初からこれを超える超大規模サイトでなければ、まずはこれで十分です。
Firestoreとの通信回数やFunctionsの利用回数 = reCAPTCHA v3の利用回数ではない
FirestoreやFunctionsと通信する度にreCAPTCHA v3のAPIが実行されるわけではありません。(もし、通信の度に実行されていたらとんでもない金額になってしまいます)
Firebase App Checkには非常に賢いキャッシュ(トークンの再利用)の仕組みが備わっているため、reCAPTCHA v3のAPI実行回数(=課金カウント)は最小限に抑えられます。
毎回実行されない仕組み
App Checkの動作は、映画館やフェスの「入場バンド(トークン)」によく例えられます。
- 最初の1回目だけreCAPTCHAを実行する
ユーザーがあなたのNext.jsサイトを開いて、最初にFirestoreからデータを読み込もうとした瞬間、App Checkは「reCAPTCHA v3」のAPIを叩いて安全性を確認し、Firebaseから「入場バンド(App Checkトークン)」を発行してもらいます。 - 2回目以降はバンドを見せるだけ(再利用)
その後、ユーザーがサイト内で別のページを開いたり、Firestoreにデータを新しく保存したり(2回目、3回目…100回目の通信)する際は、すでに持っている「入場バンド」をFirebaseに提示するだけです。裏でreCAPTCHA v3のAPIが何度も呼び出されることはありません。
reCAPTCHA v3が「再実行」されるタイミング
では、いつreCAPTCHA v3がもう一度実行されるのかというと、主に以下のタイミングだけです。
- トークンの有効期限が切れるとき
Firebaseの登録画面で設定した「トークンの有効期限」(デフォルトは1日)が近づくと、裏側で自動的にreCAPTCHA v3が1回だけ実行され、新しい入場バンドに更新(リフレッシュ)されます。 - サイトを完全に開き直したとき(リロードなど)
ユーザーがブラウザのタブを一度閉じ、時間を置いてからまたサイトにアクセスし直した際などは、再度最初の1回だけreCAPTCHA v3が実行されます。
コスト(料金)への影響は最小限
このキャッシュの仕組みがあるおかげで、「Firestoreの通信が1日1万回あったとしても、reCAPTCHA v3の消費数はユーザー数分(数10〜数100回程度)だけで済む」ということになります。
したがって、普通の人間がサイトを回遊している分には、reCAPTCHA v3の無料枠(月10,000件)を使い切ることはまずありません。
App Checkの導入方法
Next.js (App Router)環境に Firebase App Check(reCAPTCHA v3)を導入する場合、作業は以下の流れになります。
- reCAPTCHAキーの発行
- Firebaseへのキー登録
- Next.jsの環境変数(.env)へのサイトキー保存
- App-Checkパッケージのインストール
- Firebase初期化コードにApp Checkを追記する
- ローカル開発環境(localhost)の設定
- Firebase側で「適用」する(※最重要)
reCAPTCHAキーの発行
Google reCAPTCHAの管理画面にアクセスします。
> https://www.google.com/recaptcha/admin/create
まずはラベルをつけます。ラベルとは、あなた自身が、どのサイトの鍵(キー)なのかを識別するための管理用の名前(メモ)です。

reCAPTCHAタイプはv3を選択します。

今回、AppCheckを導入するアプリのドメインを入力します。

reCAPTCHAを紐づけるGCPのプロジェクトを選択します。対象のFirebaseが入っているプロジェクトを選択します。

送信ボタンをクリックします。

v3のサイトキーとシークレットキーが生成されます。

次のステップでFirebaseにシークレットキーを登録し、Next.jsのアプリ側にサイトキーを登録します。
なお、このページを閉じても後から設定ページで確認することもできます。
Firebaseへのキー登録
作成したキーをFirebaseコンソールの「シークレットキー」を登録します。
Firebaseコンソールの検索窓で「App Check」を検索します。

「使ってみる」をクリックします。

「アプリ」タブをクリックし、対象のアプリの「登録」をクリックします。

「reCAPTCHA」を選択します。

先ほど発行したシークレットキーを入力します。

「保存」をクリックすれば登録完了です。

「トークンの有効期間」と「詳細設定(アプリのリスク度)」はデフォルトのままでOKです。
トークンの有効期限
App Checkが発行する「トークンの有効期間」のことです。
- 推奨設定: 1 日ごとに送信(または 1時間 など初期値のままでOK)
- 仕組み: ユーザーがサイトに滞在している間、FirebaseのSDKが裏側で自動的にこの期限が切れる前にトークンを新しいものに更新(リフレッシュ)してくれます。そのため、長くしても短くしてもユーザーの手間は変わりません。
- 変えるべきケース: セキュリティを限界までガチガチに高めたい場合は「1時間」などに短く設定します(万が一トークンが盗まれた際のリスクを最小限に抑えるため)。ただし、短すぎると裏での通信回数がわずかに増えます。基本は「1日」のままで不都合はありません。
詳細設定:アプリのリスク度(スコアの閾値)
reCAPTCHA v3は、アクセスしてきたユーザーの挙動をAIが分析し、「0.0(完全にbot)」〜「1.0(完全に人間)」の間のスコアで判定しています。
この「アプリのリスク度」のスライダーは、「スコアが何点以下だったら『bot』とみなしてアクセスを拒否(ブロック)するか」という境界線(しきい値)を設定するものです。
- 推奨設定: 中(0.5)(初期値のまま)
- 各スコアの目安:
- 0.5(初期値): Googleが推奨する標準的なバランスです。通常のbotは確実に弾きつつ、一般のユーザーが誤って巻き添えでブロックされるリスク(誤検知)を低く抑えられます。
- 数値を上げる(例:0.7〜0.9にする): セキュリティは非常に厳しくなりますが、ブラウザの設定で拡張機能(広告ブロックなど)を入れている一般の人間まで「怪しい」と判定されてログインや登録ができなくなるリスクが高まります。
- 数値を下げる(例:0.1〜0.3にする): 判定がかなり緩くなります。一般ユーザーがブロックされる心配はほぼゼロになりますが、少し賢いbotに突破されやすくなります。
Next.jsの環境変数(.env)にサイトキーを保存する
Google reCAPTCHAの画面で発行された、もう一つのキーである「サイトキー(Site Key)」をNext.jsプロジェクトにセットします。
プロジェクトのルートディレクトリにある .env.local (または .env)ファイルを開き、以下の行を追加します。
# App Check用reCAPTCHA v3
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=ここにサイトキーを貼り付けNEXT_PUBLIC_ をつけることで、Next.jsが「これはブラウザ側(クライアントサイド)のプログラムでも使っていい変数だな」と認識します。
App-Checkパッケージのインストール
FirebaseのApp Check機能を使うために必要な公式ライブラリをインストールします。
ターミナルを開き、プロジェクトのフォルダ内で以下のコマンドを実行します。
npm install @firebase/app-checkFirebase初期化コードにApp Checkを追記する
Firebaseの初期化を行っているファイル(例:@/lib/firebase/index.ts や @/lib/firebase.ts など)を開きます。
現在書かれている初期化コードの下に、App Checkを起動するための以下のコードを追記します。
import { initializeApp, getApps, getApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';
// 1. App Checkの関数をインポート
import { initializeAppCheck, ReCaptchaV3Provider } from 'firebase/app-check';
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};
// 通常の初期化
const app = getApps().length === 0 ? initializeApp(firebaseConfig) : getApp();
export const auth = getAuth(app);
export const db = getFirestore(app);
// ── 2. ここからApp Checkの初期化を追記 ──
if (typeof window !== 'undefined') {
// ローカル開発環境(localhost)のときはデバッグモードをONにする
if (process.env.NODE_ENV === 'development') {
(window as any).FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}
initializeAppCheck(app, {
provider: new ReCaptchaV3Provider(
process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY ?? ''
),
isTokenAutoRefreshEnabled: true, // 有効期限が切れる前に自動でトークンを更新する
});
}あとは変更内容を含めてビルドして、デプロイします。
ローカル開発環境(localhost)の設定
なぜローカル開発環境(localhost)は別途設定が必要か?
本番環境では、GoogleのAI(reCAPTCHA v3)が「本物のドメイン(your-app.com)」の上で動くユーザーの挙動を監視して、正規のアクセスかどうかを賢く判定しています。
しかし、開発中の環境(localhost:3000 など)は以下のような特殊な状態にあります。
- GoogleのAIがサイトの信頼性を確認できない(インターネットに公開されていないため)
- 開発者が短時間に何度もテストのために登録やログインを繰り返す(botと同じような不審な動きに見えてしまう)
この状態のままローカルでテストをしようとすると、reCAPTCHAのAIは「怪しいアクセスだ!」と判断してスコアを0(bot)にしてしまい、App Checkに通信をブロックされてしまいます。
これでは、自分が書いたNext.jsのコードが正しく動いているのか、テストすらできなくなってしまいます。
Debug Token(デバッグトークン)の役割
そこで登場するのが Debug Token です。
あらかじめ「この文字列(トークン)を持った通信は、開発者の私だからブロックしないでね」という合言葉をFirebaseの管理画面に登録しておきます。
すると、ローカル環境でプログラムを動かした際、App Checkの検証システムはreCAPTCHAのAI判定をスキップし、「あ、登録されているデバッグトークンが送られてきたから、本物の開発者だな。通信を通してよし!」と特別に許可を出してくれます。
Debug Tokenは超強力なトークンです。Debug Tokenが外部に漏れるとApp Checkを迂回できてしまうため、.env.local 以外には記載してはいけません。
Debug Tokenの作成と登録
Firebaseコンソールに入り、App Checkの対象のアプリの「・・・」をクリックします。

「デバッグトークンを管理」をクリックします。

「デバッグトークンを追加」をクリックします。

トークン名を入力し、「トークンを生成する」をクリックし、「保存」します。

保存したトークンを.env.localに貼り付けます。
# App Check用reCAPTCHA v3 デバッグトークン
NEXT_PUBLIC_APPCHECK_DEBUG_TOKEN=保存したデバッグトークンApp Checkが正しく機能しているか確認する
App Checkを導入したら、対象アプリのブラウザのコンソールを確認します。
ここにエラーが出ていなければ正常に動いています。
AppCheck: Requests throttled due to previous 400 error.
以下のように表示された場合、400 error が原因でスロットルされています。
このエラー(警告)ログは、「直前にApp Checkの認証で400エラー(不正なリクエスト)が発生したため、Firebase側がこれ以上の無駄な通信を防ぐために、一時的にApp Checkトークンの取得リクエストをブロック(スロットリング)している状態」を意味しています。
これはreCAPTCHA Enterprise のサイトキーの設定ミスが原因であることがほとんどです。
@firebase/app-check: AppCheck: Requests throttled due to previous 400 error. Attempts allowed again after 00m:00s (appCheck/throttled).
特に、reCAPTCHAはv3とenterpriseで記述するコードが違うため、その点も注意してください。
- Enterprise用のコード:
ReCaptchaEnterpriseProvider - v3用のコード:
ReCaptchaV3Provider
Firebaseコンソールで確認する
FirebaseのコンソールのApp Checkの「API」タブを開きます。
ここに、AppCheckの結果のサマリーが表示されます。もし上手く設定できている場合は「検証済みのリクエスト」の%が増加します。
もし、設定をミスっている場合は「未検証のリクエスト」の%」増加します。

【※最重要】Firebase側で「適用」する
現時点では、AppCheck自体は機能していますが、セキュリティ機能はまだ適用されていません。全ての通信を受け入れている状態です。
正常な通信が増えた場合、FirebaseのコンソールのApp Checkの「API」タブの「モニタリング」の部分が「適用」に変わります。
「適用」ボタンをクリックすることで、AppCheckによるセキュリティ機能が有効化されます。
モニタリング中に適用する方法
モニタリング中でも、AppCheckが正しく機能していることがわかっていればセキュリティを適用することができます。
詳細を開いて「適用」をクリックします。

「App Check を適用すると、Authentication App Check トークンのないすべてのリクエストが API で拒否されます」と表示されます。
再度「適用」をクリックします。

以上でAppCheckの適用が完了です。


Firestoreのセキュリティルールに request.app != null を書く方法もありますが、適用済みの場合は、AppCheck側が先に弾くため冗長となります。
未適用の場合は実効性がありません。(クライアントが意図的にトークンを送らなければ null になるため)
このため、現在の firestore.rules への変更は不要で、Console側の操作だけで完結します。
AppCheckの適用を解除する方法
適用したAppCheckは詳細から解除することができます。


