Closed4

Firebase App Check × reCAPTCHA Enterprise の仕組み

shichishichi

Firebase App Check を Web アプリで使うとき、reCAPTCHA Enterprise プロバイダを選ぶと、ユーザーに対して追加の操作(チェックボックスや画像選択など)を強いることなく、裏側で自動的にアクセス元の信頼性を評価することができる。

裏で何が起きているのか気になって調べたのでメモしておく。

shichishichi

Firebase App Check の使い方(Web)

Firebase App Checkは、Firebaseのバックエンド(Firestore, Storage, Functions)や独自のバックエンドをボットなどから守るためのもの。

導入ステップ(Web)

import { initializeApp } from "firebase/app";
import { initializeAppCheck, ReCaptchaEnterpriseProvider } from "firebase/app-check";

const app = initializeApp({ /* Firebase 設定 */ });

initializeAppCheck(app, {
  provider: new ReCaptchaEnterpriseProvider("your-site-key"),
  isTokenAutoRefreshEnabled: true,
});
  • isTokenAutoRefreshEnabled: true をつけておくと、SDK が内部でトークンの更新を管理してくれる
  • Firebase SDK を通じた通信には、自動で X-Firebase-AppCheck ヘッダが付与される

トークンの扱い

  • App Check トークンは JWT 形式、TTL はデフォルトで 1 時間
  • TTL の半分(30 分)を超えると、自動で更新処理が走る

参考

https://firebase.google.com/docs/app-check/web/recaptcha-enterprise-provider?hl=ja

shichishichi

明示的にトークンを取得したいとき

独自のバックエンドでreCAPTCHA Enterpriseの認証を行う場合、Firebase SDK を介さない fetch や SWR のようなライブラリを使って通信することになるため、自分でトークンを取得してヘッダに付ける必要がある。

import { getToken } from "firebase/app-check";

const token = await getToken(appCheck, /* forceRefresh= */ false);

バックエンド側でトークンの認証を独自に行う。

useSWR で fetcher にトークンを入れる例

import useSWR from 'swr';
import { getToken } from 'firebase/app-check';

const fetchWithAppCheck = async (url: string) => {
  const { token } = await getToken(appCheck, false);

  const res = await fetch(url, {
    headers: {
      'X-Firebase-AppCheck': token,
    },
  });

  if (!res.ok) throw new Error("Request failed");
  return res.json();
};

const { data, error } = useSWR('/api/data', fetchWithAppCheck);
shichishichi

reCAPTCHA Enterprise のトークン発行の仕組み

reCAPTCHA Enterpriseでは、ページに以下のスクリプトが読み込まれる。

<script src="https://www.google.com/recaptcha/enterprise.js?render=your-site-key"></script>

これにより、

  • User-Agent や画面サイズなどのパッシブシグナルを収集
  • mousemove / click / keydown / scroll などのアクティブシグナルを監視

を行う。

トークン発行の流れ

grecaptcha.enterprise.ready(() => {
  grecaptcha.enterprise.execute("your-site-key", { action: "login" })
    .then(token => {
      // この token をサーバーに送るなど
    });
});

この execute() の中で

  1. これまでバッファされたシグナルをまとめて暗号化
  2. Invisible iframe 経由で Google に送信
  3. リスクスコアの判定がされる
  4. 暗号化トークン(有効期限 ≒ 2 分)が返ってくる

が実行される。

このトークンは、アクションごとに使い捨て(stateless)で、2 分以内に使う必要がある。

参考

https://cloud.google.com/recaptcha/docs/instrument-web-pages
https://cloud.google.com/recaptcha/docs/create-assessment

このスクラップは4ヶ月前にクローズされました