App Check を使用して Maps Javascript API キーを保護する

1. 始める前に

動作中のアプリケーションを示すページ

作成するアプリの概要。

この Codelab では、App Check を使用して、ウェブ環境で使用される API キーに別の保護レイヤを追加します。

具体的には、Codelab で次の手順に沿って機能を統合します。

  • Google Maps Platform Javascript API を使用して、地図をホストするウェブページを作成します。
  • オンラインでアクセスできるようにページをホストします。
  • Cloud Console を使用して、API を使用できるドメインと API を制限します。
  • Firebase を介して App Check ライブラリを追加して初期化します。
  • アプリケーションの有効性を確認する証明書プロバイダを追加します。
  • アプリとモニターでチェックを適用します。

この Codelab の最後には、使用されている API キー、アクセス元のドメイン、使用できるアプリケーションのタイプに対してセキュリティを適用するサイトが完成します。

2. 前提条件

App Check を有効にするには、保護を提供する 3 つの Google サービスを使用する必要があります。これらの領域についてよく理解しておく必要があります。

Firebase - API キーが適切なドメインから参照されていることを確認するサービスの適用を提供します。また、Firebase Studio を使用してホスティングとデプロイの機能も提供します。

reCAPTCHA - 人間がアプリケーションを使用しているかどうかを確認する機能を提供します。また、Firebase をクライアント アプリケーション ドメインに接続するための公開鍵と秘密鍵も提供します。

Google Cloud Platform - Google Maps Platform と Firebase の両方で使用される API キーと、マップキーを使用するドメインの制限を提供します。

次のアーキテクチャ図は、これらの機能がどのように連携しているかを示しています。

システムのアーキテクチャの概要

App Check と Google Maps Platform を使用する場合、次の要素が連携して、証明書プロバイダ(この場合は reCAPTCHA)から提供された証明書を使用して、リクエストが有効なアプリとユーザーから送信されたものかどうかを判断します。

これは、Firebase が提供する App Check SDK を使用して行われます。この SDK は、呼び出し元のアプリケーションの有効性を確認し、後続の Google Maps Platform Javascript API 呼び出しに使用されるトークンをアプリケーションに提供します。Google Maps Platform Javascript API は、提供されたトークンの有効性を Firebase で確認し、正しいドメインからだけでなく、証明書プロバイダ経由で有効なユーザーから送信されたものであることを確認します。

App Check と Maps JavaScript API の使用について詳しくは、以下の場所をご覧ください。必要な手順をよくご確認ください。

https://developers.google.com/maps/documentation/javascript/maps-app-check

3. セットアップする

Google Cloud アカウントをお持ちでない場合は、最初に課金を有効にしてアカウントを設定する必要があります。手順に沿って、開始前に作成してください。

Google Maps Platform を設定する

課金を有効にした Google Cloud Platform アカウントとプロジェクトをまだ作成していない場合は、Google Maps Platform スタートガイドに沿って請求先アカウントとプロジェクトを作成してください。

  1. Cloud Console で、プロジェクトのプルダウン メニューをクリックし、この Codelab に使用するプロジェクトを選択します。

  1. Google Cloud Marketplace で、この Codelab に必要な Google Maps Platform API と SDK を有効にします。詳しい手順については、こちらの動画またはドキュメントをご覧ください。
  2. Cloud Console の [認証情報] ページで API キーを生成します。詳しい手順については、こちらの動画またはドキュメントをご覧ください。Google Maps Platform へのすべてのリクエストで API キーが必要になります。

この Codelab のその他の要件

この Codelab を実行するには、次のアカウント、サービス、ツールが必要です。

  • JavaScript、HTML、CSS に関する基礎的な知識
  • 課金が有効になっている Google Cloud アカウント(前述)
  • Maps JavaScript API を有効にした Google Maps Platform API キー(これは Codelab の間に行います)。
  • ウェブホスティングとデプロイに関する基本的な知識(この Codelab で説明します)。これは Firebase コンソールと Firebase Studio を通じて行われます。
  • 作業中にファイルを表示するためのウェブブラウザ。

4. Firebase Studio でページを作成する

この Codelab では、アプリケーションがすでに作成されていることを前提としていません。Firebase Studio を使用して、地図アプリケーションをホストするページを作成し、テスト目的で Firebase にデプロイします。既存のアプリケーションがある場合は、それを使用することもできます。適切なホスト ドメイン、コード スニペット、API キーを変更して、正しく実装されていることを確認してください。

Firebase Studio(Google アカウントが必要)に移動し、新しい Simple HTML アプリケーションを作成します。このオプションを表示するには、[すべてのテンプレートを表示] ボタンをクリックする必要がある場合があります。または、このリンクをクリックして直接アクセスすることもできます。

シンプルな HTML テンプレートを示す画像

ワークスペースに適切な名前(myappcheck-map など)を付けます(一意性を確保するためにランダムな数字を追加します。これは自動的に追加されます)。Firebase Studio でワークスペースが作成されます。

新しい Workspace オプションを示す画像

名前を入力したら、作成ボタンをクリックしてプロジェクトの作成プロセスを開始します。

プロジェクト作成ダイアログを示す画像

作成したら、index.html ファイルのテキストを次のコードに置き換えます。このコードは、地図を含むページを作成します。

<!doctype html>
<html>

<head>
 <title>Secure Map</title>
 <style>
   #map {
     height: 100%;
   }

   html,
   body {
     height: 100%;
     margin: 0;
     padding: 0;
     font-family: Arial, Helvetica, sans-serif;
   }
 </style>
</head>

<body>
 <h3>App Check Security Demo</h3>
 <!--The div element for the map -->
 <div id="map"></div>
 <script>
   (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
     key: "YOUR_API_KEY",
     v: "weekly",
     // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
     // Add other bootstrap parameters as needed, using camel case.
   });
 </script>
 <script>
   let map;
   async function initMap() {
     const { Map } = await google.maps.importLibrary("maps");
     map = new Map(document.getElementById("map"), {
       center: { lat: 51.5208, lng: -0.0977 },
       zoom: 17,
     });
   }
   initMap();
 </script>
</body>

</html>

実行すると、画像に示すように、動作中のアプリの地図が表示されるはずですが、

動作中のアプリケーションを示す画像。

ページが実際に読み込まれると、エラーが表示されます。これは、ページに Google Maps Platform API キーが必要になるためです。このキーは次のセクションで追加します。

「エラーが発生しました」という通知が表示されている画像。

実際のエラー メッセージは、Firebase Studio のウェブ コンソールで確認できます。

無効な鍵のエラー メッセージ。

この問題を解決するには、ページに API キーを追加する必要があります。API キーは、ページと Maps Javascript API の実装を結び付ける方法です。また、API キーは暗号化されていない状態でページに含まれている必要があるため、悪用される可能性もあります。API キーが取得され、別のサイトで使用される可能性があります。

保護方法の 1 つは、使用されているアプリケーションのタイプまたは呼び出されているリファラー ドメインや IP アドレスを介したアプリケーションの制限を使用することです。ベスト プラクティスについて詳しくは、次のサイトをご覧ください。

https://developers.google.com/maps/api-security-best-practices#rec-best-practices

または、コマンドラインやサーバーからの直接呼び出しを使用して、リファラーを提供したり追跡したりする方法がないアプリケーション。これはセキュリティ ホールになる可能性があります。

5. Firebase アプリケーションを作成する

Firebase は、次のチェックを行うために、証明書プロバイダを関連付ける機能を提供するために使用されます。

  • 正規のアプリから送信されたリクエストであること
  • 正規の未改造のデバイスとユーザー セッションから送信されたリクエストであること。

この Codelab では、この証明のプロバイダとして reCAPTCHA v3 を使用します。

Firebase アプリケーションとホストを作成します。

https://firebase.google.com/ にアクセスし、[コンソールに移動] リンクから新しい Firebase プロジェクトを作成します。

コンソールへのリンクを示す画像

次の領域をクリックして、新しいプロジェクトを作成します。

新しい Firebase プロジェクトを作成します。

プロジェクトの名前(My App Check Project など)を選択します。これは参照用なので、以前使用した名前と同じである必要はありません。プロジェクトの実際の名前は、テキストのすぐ下で編集できます。入力した名前で構成され、一意でない場合は番号が追加されます。

プロジェクト名を入力する詳細を示す画像。

他のサービス(Google アナリティクスなど)をアプリに追加するよう求められた場合は、追加しても追加しなくてもかまいませんが、この Codelab では必要ないので、追加しなくてもかまいません。

[プロジェクトを作成] ボタンをクリックし、プロジェクトが作成されるまで待ちます。完了すると通知が届きます。

プロジェクトの作成ダイアログを示す画像。

プロジェクトの操作を開始する準備ができたら、[続行] をクリックします。

プロジェクト完了ダイアログを示す画像。

メインページで、[アプリに Firebase を追加して利用を開始しましょう] を選択し、[ウェブ] オプションを選択します。

まず、追加します。

ファイルをデプロイした後の保存先として、サイトに Firebase Hosting を設定します(実際のサイトでは独自のオプションを使用できますが、この Codelab では Firebase Hosting にデプロイします)。

新しいアプリケーションを登録します。

[アプリを登録] をクリックして、アプリケーションを作成します。次に、作成したスクリプトを使用して、ウェブ アプリケーションから Firebase のプロジェクトを参照します。

次のタブの Firebase 構成コードは、Firebase と Maps API を接続するためにアプリケーションで使用されるため、「スクリプトタグを使用する」セクションからコピーする価値があります。これをプロジェクトの index.html に貼り付けます。

ページに含めるスクリプトタグ。

他のセクションで [次へ] をクリックし、サイトのプロジェクト設定セクションで作成したアプリを確認します。

後で構成の詳細を確認する必要がある場合は、次のように [設定] ボタンからアプリの詳細を確認することもできます。

[プロジェクトの設定] メニュー項目。

このセクションを終了する前に、作成した Firebase ホスティング サイトのドメインをメモする必要があります。このドメインは、後で reCAPTCHA で使用します。これにより、サイト名を証明書プロバイダに関連付けることができ、そのサイトからのリクエストのみが検証されます。

左側のプロジェクト ショートカットまたは [ビルド] メニューからホスティング セクションに移動します。

ホスティングのショートカットを示す画像。 または ホスティング ビルド メニューを示す画像。

このセクションで、アプリケーション用に作成されたドメインを調べます。まだ設定していない場合は、いくつかの画面をクリックして設定する必要があります。

ホスティング ドメインのダイアログを示す画像。

6. API キーを保護する

Firebase で使用しているのと同じアカウントで Cloud Console にアクセスして、作成されたプロジェクトを確認します。

リンク

Cloud コンソールのリンクを示す画像

複数のプロジェクトがある場合は、プルダウンまたは検索ボックスを使用して、Firebase プロジェクトの名前で適切なプロジェクトを選択する必要があります。

[プロジェクトを選択] リストを示す画像

新しく作成されたプロジェクトが開きます。次に、Maps Javascript API をこのプロジェクトに追加して、プロジェクト内で使用できるようにします。これには、特定の API キーとホスティング ドメインに制限することも含まれます。

プロジェクトのスタートページを示す画像。

左側のメニューを使用して、プロジェクトで Maps API を有効にします。[API とサービス] と [有効な API とサービス] を選択します。

[API を有効にする] メニューが使用されている画像。

[API とサービスを有効にする] オプションを選択します。

[API を有効にする] メニューを選択している画像。

検索ボックスに「Maps Javascript API」と入力します。

API 検索ボックスを示す画像

一致する結果を選択します。

[Select matched API] ボックスを示す画像

次に、API の [有効にする] をクリックして、プロジェクトに追加します(このプロジェクトを以前に使用したことがある場合は、すでに完了している可能性があります)。

一致する API を有効にするチェックボックスを示す画像

有効にすると、API キーを追加して制限できますが、ここではスキップします。

左側のメニュー オプションをもう一度使用して、[API とサービス] セクションに戻り、作成されたブラウザキーを選択します。

API の制限を示している画像。

Maps JavaScript API を API 制限のいずれかに追加します。

フィルタリングする Maps API を選択します。

公開中のアプリケーションのキーについては、アプリケーションをホストしているドメインにも制限を設けます。Firebase で作成したドメインを使用して、今すぐ設定してください。また、ドメインの末尾に /* を追加して、その下のすべてのパスをカバーするようにしてください。

制限するドメイン。

API キーの制限の詳細については、次の場所でこの機能を有効にする方法をご覧ください。

https://developers.google.com/maps/api-security-best-practices#restricting-api-keys

7. reCAPTCHA シークレットを作成する

次のステップでは、クライアントとサーバーの両方の構成証明とキーを提供する reCAPTCHA プロジェクトを作成します。

https://www.google.com/recaptcha/ にアクセスし、[使ってみる] ボタンをクリックします。

reCAPTCHA のスタートガイドを示す画像。

次に、新しいサイトを登録します。制限するドメインを正しく入力してください。

reCAPTCHA サイトの登録を示す画像。

複数の Google Cloud プロジェクトがある場合は、Firebase が作成した Google Cloud プロジェクトが選択されていることを確認してください。

これにより、2 つのキーが作成されます。1 つは Firebase コンソールに入力するシークレット キーです。これは、一般公開されるページやアプリケーションに決して入力しないでください。もう 1 つはウェブ アプリケーションで使用するサイトキーです。

reCAPTCHA キーのページを示す画像。

このページは後で必要になるため、開いたままにしておいてください。[シークレット キーをコピー] ボタンをクリックし、Firebase サイトに戻ります。

8. Firebase に reCAPTCHA を追加する

Firebase 管理コンソールの左側のメニュー項目に移動します。[ビルド] メニュー項目で [App Check] を選択します。

Hosting のビルドメニューを示す画像。

サービスの一覧は、アプリが登録されるまで有効にできません(これは、ホスティングがサイトに追加されたときに作成されました)。設定が必要な場合は、[開始] をクリックします。

[アプリ] タブをクリックして [ウェブアプリ] を開き、reCAPTCHA サイトからコピーしたシークレットを入力して [保存] をクリックします。

シークレットを入力する様子を示す画像

reCAPTCHA プロバイダの横に緑色のチェックマークが表示されます。このウェブ アプリケーションは、reCAPTCHA を使用して、ユーザーまたはサイトがサービスを正しく呼び出しているかどうかを証明できるようになりました。

reCAPTCHA が有効になっていることを示す緑色のチェックマーク

[API] タブに、Google Maps Platform API が有効になっているが、適用されていないことが表示されます。

App Check は有効ですが、適用されていません。

これで、reCAPTCHA シークレットが Firebase プロジェクトにリンクされました。次に、ウェブページにコードを追加して、サイトキーを適切なプロバイダに照合し、マップ アプリケーションで使用できるようにします。

サイトキーは reCAPTCHA によってチェックされ、シークレット キーと照合されます。照合が完了すると、呼び出しページが正しいことが確認され、App Check は Maps JavaScript API の後続の呼び出しで使用できるトークンを提供します。この証明書がないと、トークンは提供されず、リクエストを検証できません。

9. ページに確認を追加してデプロイします。

Cloud コンソールに戻り、Maps API で使用する必要がある API キーをコピーします。

これは、コンソールのサイドメニューの [API とサービス] サイドメニューの [認証情報] オプションで確認できます。

[認証情報] メニューを示す画像。

ここから既存のブラウザキーを選択できます(ただし、前述のように、別の既存のキーを使用するか、新しいキーを作成することもできます)。

[既存のブラウザキー] オプションを示す画像。

[キーを表示] ボタンをクリックし、表示されたダイアログ ウィンドウからキーをコピーします。

先ほど作成した HTML ページが開いている Firebase Studio プロジェクトに戻ります。これで、API キーをページに追加して、ページに 「YOUR_API_KEY」がある場所で Maps API を動作させることができます。

API キーを更新する

ページを再実行すると、今度は別のエラー メッセージが表示されます。

「Referrer not allowed」エラー メッセージ

つまり、ページをホストしている開発ドメインは許可されていません(デプロイされたドメインのみが追加されています)。このサイトを Firebase Hosting を使用して正しいドメインに公開する必要があります。詳細については、以下をご覧ください。

Firebase Hosting を使用してデプロイする

この動画

Project IDX で Firebase ウェブアプリの構築、テスト、デプロイを高速化

課金が無効のエラー。

詳しくは、Maps JavaScript API サイトの地図の読み込みエラーをご覧ください。

RefererNotAllowedMapError が発生した場合は、ページを正しいドメインにデプロイすることで解決できます。

Firebase Studio に戻り、[Firebase Studio] アイコン(設定したオプションに応じて左端または右端に表示されることがあります)をクリックして、ホスティング オプションを開きます。

Firebase Studio アイコンを示す画像。

この Codelab では、次に「Firebase でアプリをホストする」ことで、Firebase インスタンスを Studio アプリケーションに接続する必要があります。

Firebase でホストするオプション。

次に、[Firebase を認証] をクリックして認証プロセスを開始します。これにより、スタジオ内からバックエンドでホスティングを自動化できるようになります。

[Authenticate Firebase] オプションを示す画像。

コマンド ウィンドウの手順に沿ってデプロイを承認します。

認証手順を示す画像。

画面上の手順(新しいウィンドウを開くなど)に沿って、認証コードの入力を求められたらコピーし、Firebase Studio のコマンド ウィンドウに貼り付けます。

Firebase 認証コードを示す画像。

このプロセスについて詳しくは、以下をご覧ください。

https://firebase.google.com/docs/studio/deploy-app

完了したら、[initialize firebase hosting] をクリックして、プロジェクトを Firebase プロジェクトにリンクします。

[既存のプロジェクトを使用する] を選択し、前のセクションで作成したプロジェクトを選択します。残りのデフォルトを受け入れます(プロジェクトの設定時に選択した名前によって、例が異なる場合があります)。

Firebase ホスティング プロジェクトの設定。

エクスプローラ ビューに戻り、public ディレクトリに作成された index.html ファイルを、ルート ディレクトリにすでにあったファイルに置き換えます。

ホスティング ファイル構造を示す画像。

Firebase Studio のサイドバーに戻り、サイトを本番環境にデプロイできるようになりました。

本番環境へのデプロイを示す画像。

これにより、コンソールにデプロイ手順が表示されます。

デプロイ手順を示す画像。

表示された [Hosting URL] からデプロイされたサイトを開きます(ここでは https://my-app-check-project.web.app/ としていますが、プロジェクトによって異なります)。

これで、使用されているドメインで API が動作するため、アプリケーションのページに地図が表示されます。

Hosting のビルドメニューを示す画像。

これで、API キーで使用できる API の種類と、API キーを使用できるドメインの両方に制約が加えられた、動作するページができました。次のステップでは、そのドメインへのアクセスをロックダウンします。これを行うには、前に生成した Firebase スクリプト セクションを追加して、App Check を使用してページを保護する必要があります。これについては次のセクションで説明します。

10. 安全なページ

現在のページでは、API キーをドメインに安全に保存しますが、正しいアプリケーションとユーザーによって使用されていることを確認するための構成証明ステップは追加されません。鍵が盗まれ、悪意のあるアクターによって使用される可能性はあります。これを停止するには、クライアントの正しいトークンを取得するために、Firebase 構成、プロバイダ、サイトキーをページに追加する必要があります。

また、Maps API の使用状況が Firebase で追跡されていることも確認できます。正しいトークンを使用していないため、未確認のリクエストが行われています。

必要な接続の詳細は、Firebase プロジェクトから取得できます。

Firebase の構成の詳細を含むコンソールから Firebase の詳細を取得します。Firebase のプロジェクト設定ページに移動し、アプリの CDN セクションで CDN 設定のコード セクション(最も簡単な方法)を取得します。

Firebase プロジェクトで、歯車アイコンを選択してプロジェクト設定を表示します。

Firebase プロジェクトの設定を示す画像

アプリの [全般] セクションに詳細が表示されたページが開きます。

Firebase アプリの構成設定。

これを、地図が含まれていてホストされている Firebase Studio ページ(public/index.html)にコピーします。次のような内容になります(このファイルの内容とまったく同じではなく、お客様の詳細情報が反映されます)。

<!doctype html>
<html>

<head>
 <title>Secure Map</title>
 <style>
   #map {
     height: 100%;
   }

   html,
   body {
     height: 100%;
     margin: 0;
     padding: 0;
     font-family: Arial, Helvetica, sans-serif;
   }
 </style>
</head>

<body>
 <h3>App Check Security Demo</h3>
 <!--The div element for the map -->
 <div id="map"></div>
 <script>
   (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
     key: "YOUR_API_KEY",
     v: "weekly",
     // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
     // Add other bootstrap parameters as needed, using camel case.
   });
 </script>
 <script type="module">
   // Import the functions you need from the SDKs you need
   import { initializeApp } from "https://www.gstatic.com/firebasejs/12.2.1/firebase-app.js";

   const firebaseConfig = {
     apiKey: "YOUR_API_KEY",
     authDomain: "appcheck-map.firebaseapp.com",
     projectId: "appcheck-map",
     storageBucket: "appcheck-map.firebasestorage.app",
     messagingSenderId: "YOUR_SENDER_KEY",
     appId: "YOUR_APP_ID"
   };
    // Initialize Firebase
   const app = initializeApp(firebaseConfig);

   let map;
   async function initMap() {
     const { Map } = await google.maps.importLibrary("maps");
     map = new Map(document.getElementById("map"), {
       center: { lat: 51.5208, lng: -0.0977 },
       zoom: 17,
     });
   }
   initMap();
 </script>
</body>

</html>

Firebase がアプリケーションに追加されたので、reCAPTCHA サイト(以前)から取得したサイトキーを使用して、reCAPTCHA ライブラリを呼び出します。

reCAPTCHA サイトキーの入力画面を示す画像。

これらのセクションを追加する方法について詳しくは、以下の Google マップのドキュメント ページをご覧ください。

https://developers.google.com/maps/documentation/javascript/maps-app-check

App Check ライブラリをページに追加し、関数を読み込んで Firebase 構成で App Check を初期化し、ReCaptchaV3Provider を使用してトークンを取得します。

まず、App Check ライブラリをインポートします。

       import {
           getToken,
           initializeAppCheck,
           ReCaptchaV3Provider,
       } from "https://www.gstatic.com/firebasejs/12.2.1/firebase-app-check.js";

次に、サイトトークンを使用して Firebase 構成と reCAPTCHA プロバイダで App Check を初期化するコードを追加します。

       // Get App Check Token
       const appCheck = initializeAppCheck(app, {
           provider: new ReCaptchaV3Provider('<INSERT SITE KEY>'),
           isTokenAutoRefreshEnabled: true,
       });

最後に、Maps Core ライブラリの設定関数を使用してトークンを取得し、関数をマップ コントロールに接続します。これにより、トークンの有効期間に応じて、マップ コントロールで必要なトークン リクエストが行われます。

       const { Settings } = await google.maps.importLibrary('core');
     Settings.getInstance().fetchAppCheckToken = () =>
           getToken(appCheck, /* forceRefresh = */ false);

完全なファイルは次のとおりです。

<!doctype html>
<html>

<head>
 <title>Secure Map</title>
 <style>
   #map {
     height: 100%;
   }

   html,
   body {
     height: 100%;
     margin: 0;
     padding: 0;
     font-family: Arial, Helvetica, sans-serif;
   }
 </style>
</head>

<body>
 <h3>App Check Security Demo</h3>
 <!--The div element for the map -->
 <div id="map"></div>
 <script>
   (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
     key: "YOUR_API_KEY",
     v: "weekly",
     // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
     // Add other bootstrap parameters as needed, using camel case.
   });
 </script>
 <script type="module">
   import { initializeApp } from "https://www.gstatic.com/firebasejs/12.2.1/firebase-app.js";

   import {
     getToken,
     initializeAppCheck,
     ReCaptchaV3Provider,
   } from "https://www.gstatic.com/firebasejs/12.2.1/firebase-app-check.js";

   const firebaseConfig = {
     apiKey: "YOUR_API_KEY",
     authDomain: "appcheck-map.firebaseapp.com",
     projectId: "appcheck-map",
     storageBucket: "appcheck-map.firebasestorage.app",
     messagingSenderId: "YOUR_SENDER_KEY",
     appId: "YOUR_APP_ID"
   };

   // Initialize Firebase
   const app = initializeApp(firebaseConfig);

   // Get App Check Token
   const appCheck = initializeAppCheck(app, {
     provider: new ReCaptchaV3Provider('<INSERT SITE KEY>'),
     isTokenAutoRefreshEnabled: true,
   });

   let map;
   async function initMap() {
     const { Map } = await google.maps.importLibrary("maps");

     const { Settings } = await google.maps.importLibrary('core');
     Settings.getInstance().fetchAppCheckToken = () =>
       getToken(appCheck, /* forceRefresh = */ false);

     map = new Map(document.getElementById("map"), {
       center: { lat: 51.5208, lng: -0.0977 },
       zoom: 17,
     });
   }
   initMap();
 </script>
</body>

</html>

Firebase Studio を使用してこれを Firebase サイトにデプロイし、ページを実行します。

11. モニタリングを適用する

これでページが設定され、実行時にチェックされることが確認できます。Firebase コンソールに戻り、[App Check] セクションを再度開きます。これで、App Check が Maps Javascript API をモニタリングするようになります。

モニタリングがオンになっていることを確認します。

ウィンドウを開くと、クライアントがリクエストを行っており、構成証明が機能していることがわかります(グラフに濃い青色の「確認済み」リクエストが表示されます)。他のリクエストでは、検証が完了する前の開発フェーズでの呼び出しが表示されます。

確認済みのリクエストを示すグラフ。

クライアントが動作していることを確認できたので、サイトで適用を有効にして、無効なクライアント アプリケーションから API キーを使用できないようにします。[適用] ボタンをクリックして適用を開始します。

強制適用ボタンを示す画像。

これをクリックすると、アプリケーションがロックダウンされることを示す大きな警告が表示されます。実際には、すべてのクライアントが正しいキーを持ち、動作していることがわかっている場合にのみこれを行います。そうでない場合、ユーザーがサイトにアクセスできなくなる可能性があります。

違反措置ダイアログを示す画像。

また、適用されるまでに時間がかかることもあります。画面に表示されているように、すぐに適用をテストすると、伝播する時間が足りない場合があります。

15 分で適用されます。

ページのリクエストを行うと、以前と同じように動作していることが確認できます。サイトには実際には何も変更されていません。

時間の経過とともに、コンソールに表示される確認済みのリクエストの数が増加します。

確認リクエストの増加を示すグラフ。

動作を確認するには、Codelab の元のサンプルに戻り、アプリチェック機能のない新しいページを作成します。このページに nocheck.html などの名前を付け、index.html と同じ場所にある public フォルダに配置します。

<!doctype html>
<html>

<head>
 <title>Secure Map</title>
 <style>
   #map {
     height: 100%;
   }

   html,
   body {
     height: 100%;
     margin: 0;
     padding: 0;
     font-family: Arial, Helvetica, sans-serif;
   }
 </style>
</head>

<body>
 <h3>App Check Security Demo</h3>
 <!--The div element for the map -->
 <div id="map"></div>
 <script>
   (g => { var h, a, k, p = "The Google Maps JavaScript API", c = "google", l = "importLibrary", q = "__ib__", m = document, b = window; b = b[c] || (b[c] = {}); var d = b.maps || (b.maps = {}), r = new Set, e = new URLSearchParams, u = () => h || (h = new Promise(async (f, n) => { await (a = m.createElement("script")); e.set("libraries", [...r] + ""); for (k in g) e.set(k.replace(/[A-Z]/g, t => "_" + t[0].toLowerCase()), g[k]); e.set("callback", c + ".maps." + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; a.onerror = () => h = n(Error(p + " could not load.")); a.nonce = m.querySelector("script[nonce]")?.nonce || ""; m.head.append(a) })); d[l] ? console.warn(p + " only loads once. Ignoring:", g) : d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n)) })({
     key: "YOUR_API_KEY",
     v: "weekly",
     // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
     // Add other bootstrap parameters as needed, using camel case.
   });
 </script>
 <script>
   let map;
   async function initMap() {
     const { Map } = await google.maps.importLibrary("maps");
     map = new Map(document.getElementById("map"), {
       center: { lat: 51.5208, lng: -0.0977 },
       zoom: 17,
     });
   }
   initMap();
 </script>
</body>

</html>

この操作を行い、正しい API キーを入力すると、ページをリクエストしたときに(yourdomain/nocheck.html を使用)、次のグレーのエラー ボックスが表示されます。

「エラーが発生しました」というエラー。

コンソールを確認すると、次のようなエラー メッセージが表示されます。

アプリのチェックが無効である場合のエラー メッセージ

アプリチェックは、強制適用されたサイトのアプリチェック トークンを取得しなくなったため、ページ上の地図のリクエストを正常にブロックしました。

12. 完了

サイトで App Check を有効にしました。

動作中のアプリケーションを示すページ

Firebase App Check を使用して、リクエストが有効なドメインとユーザーから送信されたことを確認するアプリケーションを構築しました。

学習した内容

  • Firebase Studio を使用してウェブページをホストし、デプロイする方法。
  • Cloud コンソールを使用して Google Maps Platform API を有効にして保護する方法。
  • reCAPTURE を使用して、呼び出しの証明に使用できるキーを生成する方法。
  • Firebase App Check を使用して Maps JavaScript API に統合する方法。
  • Firebase Studio を使用して、保護されたサイトへの呼び出しを強制し、モニタリングする方法をご覧ください。

次のステップ

  • Google Maps Javascript API の App Check のドキュメントをご覧ください。
  • Firebase の App Check について詳しくは、こちらをご覧ください。
  • App Check と Google Maps Places API を使用した別の Codelab を試す。
  • reCAPTCHA について詳しく説明します。