はじめに
ジークレスト エンジニアの森です。この度はGoogleから提供されている「Play Integrity API」の導入方法や実装してみて気づいた知見について共有します。
GoogleのSafetyNetが廃止となり、Play Integrity APIに移行しようとしている方も多いと思います。これから移行しようと思っていたり新規で実装しようと思っているエンジニア向けの記事として、困りそうな実装の細部にフォーカスしていくつかのトピックを紹介しようと思います。
※こちらの情報は2023年06月段階の情報であることにご注意ください。
Play Integrity APIとは
Play Integrity API は、不正行為や不正アクセスなど、潜在的に危険で不正な操作からアプリやゲームを保護するのに役立ちます。この API により、デベロッパーは、攻撃を回避し不正行為を減らすための適切な措置を講じることができます。
Play Integrity API の概要
公式からの説明にありますが、SafetyNetAPIの廃止後にそれに変わるセキュリティ強化のための機能です。このAPIを実施することで以下の要件を手軽に検証できます。
- 正規のアプリバイナリ: Google Play で認識されている、改変されていないバイナリとやり取りしているかどうかを判断します。
- 正規の Play インストール: 現在のユーザー アカウントにライセンスが付与されているかどうか(つまりユーザーが Google Play でアプリまたはゲームのインストールや支払いを行ったかどうか)を判断します。
- 正規の Android デバイス: Google Play 開発者サービスを搭載した正規の Android デバイスでアプリが動作しているかどうかを示します。
実装の概要
APIの実装イメージや詳細な実装方法については、Google公式ページのAPIの使用方法の概要を参考にすると良いと思います。
Play Integrity APIを実装する場合にクライアント・サーバで実装を行う必要がある箇所は以下の通りです。
- (サーバ) app serverからNonce生成をして、Appに送信する処理
- (クライアント) AppからIntegrity APIを実行し、tokenを受け取る処理
- (クライアント) 返却されたtokenをApp serverに伝える処理
- (サーバ) クライアントから伝えられたTokenを復号する処理 もしくは GoogleのサーバにTokenを送信して復号する処理
またこれらの各セクションの処理については公式ページにサンプルが用意されています。以下リンク先では、各言語やUnityでの実装例が紹介されています。クライアント・サーバどちらの処理も記載されていますので、こちらを参考に実装を進めるとスムーズにPlay Integrity APIの組み込みができると思います。

設定や実装で気をつけるべき点
APIを実装したアプリをリリースするに当たっていくつかつまづいた点がありました。以下トピックに分けて知見をご紹介します。
APIの割当(使用回数上限)について
アプリには、アプリの使用量ティアに基づいて APIへの1日あたりのリクエスト数の上限が割り当てられます。標準ティアのアプリは、Integrity API に 1 日あたり最大 10,000 件のリクエストを行えます。
Play Integrity API を使用して危険な操作を検出し不正使用から保護する
APIには使用回数上が決まっています。APIを有効化した直後でしたら10000件/日の制限があります。割当の量が妥当かどうか事前に検討しておきましょう。もし使用量ティアを更新したければ専用のフォームを使って申請を行います。フォームはAPI未実装の段階からでも提出できますので割当量が事前に把握できている場合は申し込みしておきましょう。
またゲーム向け Google Play パートナー プログラムのデベロッパーである場合はほぼ無制限のAPI割当が特典としてもらえます。特典は事前に申請も不要で、API有効化と同時に適用されます。
クライアント側のAPI実行についての工夫
APIの実行については以下の点を意識するといいかもしれません。
- 起動時に一度だけ呼ぶような仕組みにすること
- 非同期で実行するような仕組みにすること
Play Integrity APIは基本的には起動時に検証できれば十分です。ゲームアプリの場合は起動時やログイン時といったゲーム中に一度しか実行しないフローがあると思いますのでそちらに組み込むのが良いと思います。
またAPIは非同期実行することを忘れないようにしましょう。APIはバックグラウンドで実行しても構いません。またGoogle側と通信するのでネットワーク不良やGoogle側のサーバエラー等でAPIが失敗したとしても適切にリトライやエラー処理を行うのも忘れないようにしましょう。APIが失敗した際にアプリが操作不能になるようなことは避けるようにできると良いですね。
検証環境を用意する場合の工夫
アプリによっては検証環境と本番環境を分けて開発している場合もあると思います。検証環境を用意する場合の注意点についても知見を共有しておきます。
呼び出し先の振り分け
クライアントがIntegrity APIを実行しtokenを受け取るAPI初期化処理時に、GoogleCloudPlatformプロジェクト番号をオプションとして入力できます。このプロジェクト番号を使い分けることでAPI接続先をコントロールすることができます。例えば検証用プロジェクトの番号をAPIマネージャーへ登録することで、GoogleCloudPlatformへ直接APIを投げることができます。検証時の疎通確認としてはGoogleCloudPlatformプロジェクトの番号を渡すやり方がおすすめです。こちらの詳細については以下の「IntegrityTokenRequest」のリファレンスから確認できます。

PlayConsoleとの連携確認
GoogleCloudPlatformだけでなくPlayConsoleとGoogleCloudPlatformをリンクさせた疎通確認を行いたい場合は仮のプロジェクトを作成する必要があります。以下のような手順を踏むことでPlayConsoleを経由した疎通確認ができました。
- PlayConsoleからテスト用プロジェクトを用意する
- テスト用のGoogleCloudPlatformプロジェクトを用意する
- GoogleCloudPlatformからAPI有効化
- PlayConsoleからAPIを有効化し、GoogleCloudPlatformプロジェクトとリンクを行う
- テスト用のAndroid AABビルドを作成し、PlayConsoleにアップロードする
- PlayConsoleから内部テスト版としてビルドをリリースする
- Android実機から内部テスト版としてアプリをダウンロードしてAPIを実行する
注意点としては、PlayConsoleを経由したAPIを投げるためには内部テスト版をリリースする必要がある点です。理由については以下「トークンデコードの手法とその注意点」の節で補足します。
Nonce(ノンス)の仕様について
Play Integrity APIを使用する際には、Nonceを自前で生成する必要があります。生成したNonceは、APIを叩いた後に得られるTokenをデコードした際にも取得でき、APIを叩く際に使用したNonceと応答で得られるNonceを比較することで通信が改ざんされていないかを検証できます。さて、このNonceは以下のフォーマットが指定されています。
整合性関連のノンスは、Base64 ベース エンコード、URL セーフ、かつラップなし
https://developer.android.com/google/play/integrity/migrate?hl=ja#nonce-encodingString
として Play Integrity API に渡す必要があります。この形式は、byte[]
を必要とする SafetyNet Attestation API とは異なります。
実装して気がついたことですが、このフォーマットに加えてBase64のパディングありであることに気をつけましょう。APIに添付するNonceはパディングなしで送信することも可能でした。ただし応答として得られるNonceにはパディングありとなります。送信と応答のフォーマットを合わせるという意味でも、生成するNonceはパディングありのBase64フォーマットにしておくのをおすすめします。
トークンデコードの手法とその注意点
APIを叩いたあとに返却されるTokenについては、Google側のサーバで復号する方法とセルフマネージドで自身のサーバで復号する2種類の方法が取れます。どちらも得られる結果は変わりませんが各手法でチェックしておくべきポイントがあります。

まずGoogle側のサーバで復号する方法です。Googleから推奨されている方法ですので差し支えなければこの方法を使いましょう。注意点としてはデコードする際にはGoogleCloudPlatformのAPI割当量が消費されてしまう点です。APIの割当(使用回数上限)が存在しているため、こちらの割当が足りない場合は事前に上限緩和申請を行っておきましょう。
セルフマネージドの手法の場合はGooglePlayから暗号化キーを登録する必要があります。セルフマネージド用の追加セットアップがありますので公式ページの説明に沿って対応しましょう。
またどちらの復号方式でも、開発用のAPKアプリでは正しく復号できませんでした。Google側のサーバでは復号は成功するのですが、復号後の完全性の検証結果が不正であるという内容になっていました。またセルフマネージドでの復号の場合はそもそも復号自体が失敗するという結果でした。復号を正しく検証するためには、内部テスト版で配信する等PlayGamesから配信を行わないとだめなようです。リリース前のデコード検証をする際には注意しましょう。
Google側のサーバでToken復号 | セルフマネージドサーバでTokenを復号 | |
開発アプリとしてapkをインストール | ▲(復号は成功するが検証失敗として扱われる) | ☓(復号失敗する) |
内部テストとしてPlayGamesから配信 | ◯ | ◯ |
完全性の検証結果について
Tokenを復号すると以下のような情報を含んだJsonとして完全性の検証結果が取得できます。
返されるペイロードは書式なしテキスト JSON であり、デベロッパー提供の情報とともに完全性シグナルを含んでいます。
ペイロードの一般的な構造は次のとおりです。
{https://developer.android.com/google/play/integrity/verdict?hl=ja#returned-payload-format
requestDetails: { ... }
appIntegrity: { ... }
deviceIntegrity: { ... }
accountDetails: { ... }
}
リクエストの詳細フィールドのリクエストに関する情報と、アプリの完全性フィールド、デバイスの完全性フィールド、アカウントの詳細フィールドの3つのセキュリティに関する情報が得られます。後者の3つのフィールドで、ユーザが正規のアプリケーションを使用しているのか、Root化の実施や仮想デバイスでのプレイをしている可能性や、GooglePlayから正規の手順でアプリをダウンロードしているのかを検証できます。
またこの情報はPlayConsoleとも連携でき、Console上から統計レポートを確認したり、信頼できないデバイスをGoogle Playの配信対象から自動で除外したりすることができます。以下の図はPlayConsoleからテスト用プロジェクトを生成し、統計レポートと配信対象の除外設定を確認したものです。


まとめ
実際にPlay Integrity APIを実装してみると、リファレンスに記載されている以外の細かい部分でつまづくことが多かったです。細かい部分のネタをご紹介しましたが、皆さんが実装で困った際の手助けになれば幸いです。