Play Integrity API 統合ガイド

早期アクセス プログラム(EAP)

Play Integrity API は現在、早期アクセス プログラムを通じて利用できます。一般提供はされておらず、積極的な開発が行われています。問題やフィードバックについては、integrity-api-eap@google.com までメールでご連絡ください。

Play Integrity API 利用規約

Play Integrity API にアクセスするか、またはこれを使用すると、Play Core ソフトウェア開発キット利用規約に同意したことになります。適用されるすべての利用規約とポリシーを確認し、理解したうえで API にアクセスしてください。

概要

Play Integrity API は、危険性のある不正なトラフィックの検出に使用できる Google Play の不正使用対策機能を統合したものです。そのようなトラフィックとしては、未承認バージョンのアプリやゲーム、信頼できないデバイス、信頼できない環境からのものが考えられます。これを検出することで、各種不正行為や不正アクセスなどの不正使用を防止するための適切な措置を取ることができます。この API はアプリとゲームの両方に使用できます。ゲームで使用する場合は、Google for Games デベロッパー サミット 2021 のこちらの紹介動画をご覧いただけます。

現在、この API からは、次の情報を含む署名付き暗号化レスポンスが提供されます。

Google Play と Android デバイスでの完全性の仕組みをハイライト表示した完全性の画像
  • アプリの完全性: Google Play で認識済みの、改変されていないバイナリとやり取りしているかどうかを示します。
  • アカウントの詳細: 現在のユーザー アカウントがライセンスを付与されているかどうか、つまり、そのアプリやゲームを Google Play からインストールまたは購入したかどうかを示します。
  • デバイスの完全性: Google Play 開発者サービスを搭載した正規の Android デバイスでアプリが実行されているかどうかを示します。

API の使用方法の概要

アプリ、アプリのバックエンド、Play Integrity API の関係を示す図

大まかには、Play Integrity API は次のように動作します。

  1. アプリのバックエンドが、一意のノンスを生成してアプリに送信します。
  2. アプリがノンスを使用して Integrity API を呼び出し、Play の完全性ガイダンスを含む署名付き暗号化トークンを受け取ります。
  3. アプリがバックエンドにこのトークンを送信します。
  4. アプリのバックエンドはトークンを復号して検証し、トークン ペイロードに含まれるシグナルに基づいて処理方法を決定します。
  5. アプリのバックエンドが、決定結果をアプリに送信します。

セキュリティ上の考慮事項

Play Integrity API はセキュリティの確保と改ざん防止に役立ちますが、復号と検証は必ず安全なサーバー環境で行うようにしてください。セキュリティの詳細情報がクライアント アプリを介して開示されると、攻撃者によって APK やリポジトリから抽出、削除される可能性があります。具体的には、次の点にご注意ください。

  • 主にこの API を呼び出すのは、ユーザー エクスペリエンスの重要な要素となる高価値で繰り返しのないアクション(サービスへのログイン、マルチプレーヤー型ゲーム サーバーへの参加など)を保護する場合です。完全性トークンを頻繁に取得することは、特に古いデバイスではその生成に時間がかかるため、おすすめできません。1 日のうちに高価値のアクションを複数回行うユーザーには複数回の呼び出しが必要になりますが、アクティブ ユーザー 1 人あたりの呼び出し回数は、平均して 1 日に 1 回が妥当です。
  • ノンスは一意で、攻撃者が予測できないものにします。
  • すべての復号と検証は、安全なサーバー環境で行います。受信したトークンをクライアント アプリ内で復号したり検証したりしないでください。
  • クライアント アプリに復号鍵を開示しないでください。
  • サーバーからアプリに対しては、成功または失敗のレスポンスを 1 回で返すのではなく、多数のシグナルと決定結果を送信して複製を困難にするようおすすめします。たとえば、「許可」、「制限付きで許可」、「reCAPTCHA 処理後に制限付きで許可」、「拒否」のような一連の関連レスポンスを使用します。

Play Integrity API は、不正使用対策戦略の一環として他のシグナルと併用するのに適しており、それだけを不正使用対策メカニズムとして使用するものではありません。常にアプリに適したセキュリティのベスト プラクティスと組み合わせて使用する必要があります。

API の使用制限

API のリクエスト回数には、呼び出し元アプリに割り当てられた使用量ティアに基づいて、アプリごとに 1 日あたりの最大数が適用されます。EAP 内のアプリには、特別な EAP 使用量ティアが自動的に割り当てられます。

EAP 使用量ティアでは、リクエスト数はアプリごとに 1 日あたり 500 万回に制限されます。1 日あたり 500 万回を超えるリクエストが必要な場合は、integrity-api-eap@google.com までお問い合わせください。

Google Play で配布するアプリを、他のアプリストアや配布チャネルでも配信する場合、API の使用に関してはすべて 1 つのアプリとしてカウントされます。Google Play とそれ以外の配布チャネルで異なるパッケージ名を使用する場合は、integrity-api-eap@google.com に連絡して、Google Play 以外のパッケージ名を Google Play のパッケージ名にリンクするものとして登録してください。

使用量ティアの詳細については、API が一般提供されるときに提供されます。その時点で、EAP 使用量ティア内のアプリは、パブリック ティアに移行されます。

前提条件

統合の要件

デバイスの要件

  • Android 4.4(KitKat、API レベル 19)以降
  • Play ストア アプリ 24.6.31 以降
  • Play 開発者サービス V19(12800000)以降

完全性トークンキーを取得する

セキュリティ上の理由から、受信したトークンキーは暗号化されています。完全性トークンキーを取得して復号する手順は次のとおりです。

  1. 新しい秘密鍵と公開鍵のペアを作成します。なお、RSA 鍵は 2,048 ビットでなければなりません。

    $ openssl genrsa -aes128 -out private.pem 2048
    $ openssl rsa -in private.pem -pubout > public.pem
    
  2. 公開鍵を integrity-api-eap@google.com にメールで送信し、暗号化された完全性トークンキー(integrity_token_keys.enc)を受信するまで待ちます。

  3. 秘密鍵を使用して、完全性トークンキーを復号します。

    $ openssl rsautl -decrypt -oaep -inkey private.pem \
        -in integrity_token_keys.enc > integrity_token_keys.txt
    $ cat integrity_token_keys.txt
    DECRYPTION_KEY=XXXXXXXXXXXXX
    VERIFICATION_KEY=YYYYYYYYYYYYY
    

Play Core をアプリに統合する

Play Core Library は、Google Play ストアに対するアプリのランタイム インターフェースです。Play Core をアプリに統合するには、Google Play Core Library の概要の手順を行います。

Java または Kotlin

EAP 期間中は、Google の Maven にあるものではなく、前提条件に記載されているバージョンのライブラリを使用する必要があります。

ローカル Maven リポジトリを使用している場合は、次の依存関係をアプリの build.gradle ファイルに追加します。

implementation com.google.android.play:core-integrity

Unity

Play Unity プラグインの最新リリースを使用する代わりに、前提条件に記載されている .unitypackage ファイルまたは .tgz ファイルを Unity プロジェクトにインポートしてください。インストール手順については、Unity 用の Google パッケージのインストールに関するページをご覧ください。

ネイティブ

Play Core Native Library の開発環境設定ガイドをご覧ください。

ノンスを生成する

ノンスとは、1 つのリクエストのためだけに生成される、1 回限りの一意のトークンです。これにより、Play Integrity API へのリクエストがリプレイ攻撃から保護されます。たとえば、ユーザーが特定の保護対象アクション用に Play Integrity API トークンを取得した場合、それが以降の他の保護対象アクションに再利用(「リプレイ攻撃」と呼ばれます)できてはなりません。そのため、ノンスは一意かつ予測不可能である必要があります。

ノンスの作成に推奨される方法は、安全なバックエンド サーバーで、クライアント固有の情報を一切含まない暗号論的に安全な乱数を生成することです。安全性は劣るものの、タイムスタンプやユーザー ID などのデータを組み合わせて暗号ハッシュを計算し、その値をノンスとしてクライアント アプリに送信するという方法もあります。

ノンスの値は、そのまま Google のシステムに渡されます。したがって、ノンスに個人情報などの機密データが直接的または間接的に含まれないようにすることが重要です。場合によっては、ハッシュされたデータからでも機密データが収集される可能性があるため、特に機密性の高いアプリの場合は機密データを使用しないでください。

ノンスの生成は、サーバー上でもクライアント上でも可能です。しかし、クライアントは信頼できるとは限りません。攻撃者のものである場合もあるからです。そのため、クライアント側よりも、サーバー側でノンスをランダムに生成する方が安全です。

サーバー生成のノンス

最も安全なのは、受信した各クライアント リクエストに対し、安全なサーバー環境で、暗号論的に安全な乱数ジェネレータを使用してノンスを生成するという方法です。次の図に例を示します。

サーバーを使用したノンス生成の例

ノンスは一意かつ予測不可能であるため、この方法からはリクエストがリプレイできないという確実な保証が得られます。

クライアント生成のノンス

クライアントによっては、サーバー側にすべての受信リクエストを保持できなかったり、サーバーとのやり取りによる遅延に耐えられなかったりする場合があります。そのような場合を想定して、ある程度のリプレイ保証と引き換えにシンプルさを採択する方法を、次の例に示します。この例では、リクエストに含まれるリクエスト パラメータ(タイムスタンプなど)をハッシュしています。

クライアント デバイスでのノンス生成の例

同じリクエストでもノンスが同じにならないよう、リクエスト パラメータにはタイムスタンプを含める必要があります。これにより、ノンスは確実に一意になります。それでも、タイムスタンプとリクエスト パラメータはクライアント上で生成され、そのクライアントは攻撃者のものである場合もあるため、悪意のある行動の可能性は否定できません。

ノンス生成の手法は、保護対象アクションの価値と、アプリに対して予測される脅威に応じて選択する必要があります。

完全性トークンを取得する

ノンスを生成したら、次の例のようにまず IntegrityManager を作成することで、完全性トークンを取得できます。作成したマネージャーを使用して requestIntegrityToken() を呼び出し、関連付けられた IntegrityTokenRequest ビルダーの setNonce() メソッドでノンスを指定します。

Java プログラミング言語の例

// Receive the nonce from the secure server.
String nonce = ...

// Create an instance of a manager.
IntegrityManager integrityManager =
    IntegrityManagerFactory.create(applicationContext);

// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> integrityTokenResponse =
    integrityManager
      .requestIntegrityToken(
          IntegrityTokenRequest.builder().setNonce(nonce).build());

Unity の例

IEnumerator RequestIntegrityTokenCoroutine()
{
    // Receive the nonce from the secure server.
    var nonce = ...

    // Create an instance of a manager.
    var integrityManager = new IntegrityManager();

    // Request the integrity token by providing a nonce.
    var tokenRequest = new IntegrityTokenRequest(nonce);
    var requestIntegrityTokenOperation =
        integrityManager.RequestIntegrityToken(tokenRequest);

    // Wait for PlayAsyncOperation to complete.
    yield return requestIntegrityTokenOperation;

    // Check the resulting error code.
    if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
    {
        AppendStatusLog("IntegrityAsyncOperation failed with error: " +
                        requestIntegrityTokenOperation.Error);
        yield break;
    }

    // Get the response.
    var tokenResponse = requestIntegrityTokenOperation.GetResult();
}

ネイティブの例

/// Create an IntegrityTokenRequest opaque object.
const char* nonce = RequestNonceFromServer();
IntegrityTokenRequest* request;
IntegrityTokenRequest_create(&request);
IntegrityTokenRequest_setNonce(request, nonce);

/// Prepare an IntegrityTokenResponse opaque type pointer and call
/// IntegerityManager_requestIntegrityToken().
IntegrityTokenResponse* response;
IntegrityErrorCode error_code = IntegrityManager_requestIntegrityToken(request, &response);

...

/// Proceed to polling iff error_code == INTEGRITY_NO_ERROR
if (error_code != INTEGRITY_NO_ERROR) {
    /// Remember to call the *_destroy() functions.
    return;
}

...

/// Use polling to wait for the async operation to complete.
IntegrityResponseStatus response_status;
IntegrityErrorCode error_code = IntegrityTokenResponse_getStatus(response, &response_status);
if (error_code == INTEGRITY_NO_ERROR && response_status == INTEGRITY_RESPONSE_COMPLETED) {
    const char* integrity_token = IntegrityTokenResponse_getToken(response);
    SendTokenToServer(integrity_token);
}

...

/// Remember to free up resources.
IntegrityTokenRequest_destroy(request);
IntegrityTokenResponse_destroy(response);
IntegrityManager_destroy();

トークンを復号して完全性を検証する

提供したノンスは、返される署名付きレスポンス トークンの一部になります。返されるトークンは、IntegrityTokenResponse#token() メソッドを使用して取得できます。返されたトークンを、安全なサーバー環境内で復号して検証します。決してアプリ内では行わないでください。

トークン形式

このトークンは、ネストされた JSON ウェブトークン(JWT)、つまり JSON Web Signature(JWS)JSON Web Encryption(JWE)です。JWE と JWS のコンポーネントは、Compact Serialization で表現されます。

暗号化と署名のアルゴリズムは、次のようなさまざまな JWT 実装で適切にサポートされています。

  • JWE は alg に A256KW を、enc に A256GCM を使用
  • JWS は ES256 を使用

次の例は、Google Play Console またはメールで取得した AES 鍵と DER エンコードされた署名検証用の公開 EC 鍵を、アプリのバックエンドで言語(この例では Java)固有の鍵に復号する方法を示したものです。なお、鍵はデフォルトのフラグを使用して Base64 エンコードされています。

// base64OfEncodedDecryptionKey is provided through Play Console / over email.
byte[] decryptionKeyBytes =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT);
// Deserialized encryption (symmetric) key.
SecretKey decryptionKey =
    new SecretKeySpec(
        decryptionKeyBytes,
        /* offset= */ 0,
        AES_KEY_SIZE_BYTES,
        AES_KEY_TYPE);

// base64OfEncodedVerificationKey is provided through
//  Play Console / over email.
byte[] encodedVerificationKey =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT);
// Deserialized verification (public) key.
PublicKey verificationKey =
    KeyFactory.getInstance(EC_KEY_TYPE)
        .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));

次に、これらのキーを使用して完全性トークン(JWE の部分)を復号してから、ネストされた JWS の部分を検証して抽出します。

JsonWebEncryption jwe =
 (JsonWebEncryption)JsonWebStructure.fromCompactSerialization(integrityToken);
jwe.setKey(decryptionKey);

// This also decrypts the JWE token.
String compactJws = jwe.getPayload();

JsonWebSignature jws =
    (JsonWebSignature) JsonWebStructure.fromCompactSerialization(compactJws);
jws.setKey(verificationKey);

// This also verifies the signature.
String payload = jws.getPayload();

その結果、完全性シグナルを含む書式なしテキストのトークンがペイロードとして得られます。

さらに、JSON ペイロードの requestDetails 部分も検証する必要があります。それには、ノンスとパッケージ名が元のリクエストで送信したものと一致すること、およびタイムスタンプが古くないことを確認します。

JSONObject requestDetails =
    new JSONObject(payload).getJSONObject("requestDetails");
String requestPackageName =
    requestDetails.getString("requestPackageName");
String nonce = requestDetails.getString("nonce");
long timestampMillis = requestDetails.getLong("timestampMillis");
long currentTimestampMillis = ...;

if (!requestPackageName.equals(expectedPackageName)
    // See "Generate nonce" section of the doc on how to store/compute
    // the expected nonce.
    || !nonce.equals(expectedNonce)
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
  // The token is invalid!
  ...
}

返されるペイロードの形式

返されるペイロードは書式なしテキスト JSON であり、デベロッパー提供の情報とともに完全性シグナルを含んでいます。

一般的なペイロード構造は次のとおりです。

{
    requestDetails: { ... }
    appIntegrity: { ... }
    deviceIntegrity: { ... }
    accountDetails: { ... }
}

以下のセクションでは、各フィールドについて詳しく説明します。

リクエストの詳細フィールド

requestDetails フィールドには、ノンスなど、リクエストで指定された情報が含まれます。これらの値は、元のリクエストの値と一致する必要があります。

requestDetails: {
    // Application package name this attestation was requested for.
    // Note that this field might be spoofed in the middle of the
    //  request.
    requestPackageName: "com.package.name"
    // base64-encoded web-safe no-wrap nonce provided by the developer.
    nonce: "aGVsbG8gd29scmQgdGhlcmU"
    // The timestamp in milliseconds when the request was made
    //  (computed on the server).
    timestampMillis: 1617893780
}

アプリの完全性フィールド

appIntegrity フィールドにはパッケージに関する情報が含まれます。

appIntegrity: {
    // PLAY_RECOGNIZED, UNRECOGNIZED_VERSION, or UNEVALUATED.
    appRecognitionVerdict: "PLAY_RECOGNIZED"
    // The package name of the app.
    // This field is populated iff appRecognitionVerdict != UNEVALUATED.
    packageName: "com.package.name"
    // The sha256 digest of app certificates on device.
    // This field is populated iff appRecognitionVerdict != UNEVALUATED.
    certificateSha256Digest: ["6a6a1474b5cbbb2b1aa57e0bc3"]
    // The version of the app.
    // This field is populated iff appRecognitionVerdict != UNEVALUATED.
    versionCode: 42
}

appRecognitionVerdict は次のいずれかの値を取ります。

  • PLAY_RECOGNIZED: アプリと証明書が、Google Play で配信されているバージョンと一致します。
  • UNRECOGNIZED_VERSION: 証明書またはパッケージ名が Google Play の記録と一致しません。
  • UNEVALUATED: アプリの完全性は評価されませんでした。要件が満たされていません(デバイスが信頼できないなど)。

デバイスの完全性フィールド

deviceIntegrity フィールドには deviceRecognitionVerdict という値のみが含まれます。この値は、デバイスがアプリの完全性をどの程度強化できるかを表します。

deviceIntegrity: {
    // "MEETS_DEVICE_INTEGRITY" is one of several possible values.
    deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
}

デフォルトでは、deviceRecognitionVerdict は次のいずれかのラベルを持ちます。

  • MEETS_DEVICE_INTEGRITY: アプリは、Google Play 開発者サービスを搭載した GMS Android デバイスで実行されています。このデバイスはシステム完全性チェックに合格し、Android の互換性要件を満たしています。
  • MEETS_VIRTUAL_INTEGRITY: アプリは、PC 版 Android(AoPC)など、Google Play 開発者サービスを備えた仮想 Android 環境で実行されています。この環境は Android の互換性に関する主要な要件を満たしており、Google Play の完全性チェックに合格しています。現在 PC 版 Android の早期アクセス プログラムに参加していない場合は、このラベルを無視できます。
  • ラベルなし(空の値など): アプリは、API フックなどの攻撃やルート権限取得などのシステム侵害の兆候があるデバイス、または Google Play の完全性チェックに合格していない仮想デバイス(エミュレータなど)で実行されています。

パートナーは、integrity-api-eap@google.com にメールで連絡すれば、deviceRecognitionVerdict で利用可能な追加のラベル(下記)を受け取れるようになります。

  • MEETS_BASIC_INTEGRITY: アプリは基本的なシステム完全性チェックに合格したデバイスで実行されています。このデバイスは Android の互換性要件を満たしておらず、Google Play 開発者サービスの実行を承認されていない可能性があります。たとえば、認識されていないバージョンの Android を実行している、ロック解除されたブートローダーを搭載している、メーカーによる認証を受けていない、などの可能性がある場合が該当します。
  • MEETS_STRONG_INTEGRITY: アプリは、Google Play 開発者サービスを搭載した GMS Android デバイスで実行されており、ハードウェア格納型キーストアのような高いレベルのシステム完全性を確保しています。このデバイスはシステム完全性チェックに合格し、Android の互換性要件を満たしています。

1 つのデバイスが複数のラベルの条件を満たす場合、完全性レスポンスに複数のラベルを含めることができます。複数のラベルを受け取るようオプトインすることで、さまざまな信頼度に応じたデバイスへの対応戦略を実装できます。

たとえば、MEETS_BASIC_INTEGRITYMEETS_DEVICE_INTEGRITYMEETS_STRONG_INTEGRITY を返すデバイスを考えてみましょう。このデバイスは MEETS_BASIC_INTEGRITY のみを返すデバイスよりも信頼度が高いため、それに応じた対応をすれば済むでしょう。

なお、インターネット接続が不安定、デバイスの負荷が高いなど、特定の環境条件下ではデバイスの完全性チェックが失敗する場合があります。その場合、実際には信頼度が高いデバイスにラベルが生成されないことがあります。こうしたシナリオの影響を軽減するには、再試行オプションを含めるようにしてください。

アカウントの詳細フィールド

accountDetails フィールドには licensingVerdict という値のみが含まれます。この値は、アプリのライセンスまたは利用資格のステータスを表します。

accountDetails: {
    // This field can be LICENSED, UNLICENSED, or UNEVALUATED.
    licensingVerdict: "LICENSED"
}

licensingVerdict は次のいずれかの値を取ります。

  • LICENSED: ユーザーはアプリの利用資格を持っています(たとえば、Google Play から、または Play Pass を通じてインストールまたは購入した)。
  • UNLICENSED: ユーザーはアプリの利用資格を持っていません(サイドローディングした、Google Play から入手していないなど)。
  • UNEVALUATED: 要件が満たされていないため、ライセンスの詳細は評価されませんでした(デバイスが十分に信頼できない、アプリが Google Play に認識されていないなど)。

エラーコード

Task<IntegrityTokenResponse> integrityTokenResponse = ...;
integrityTokenResponse.addOnFailureListener(error -> ...);

次の表に、API が返すエラーと、推奨される対応手順を示します。

エラーコード 説明 対応手順
API_NOT_AVAILABLE Integrity API を使用できません。Play ストアのバージョンが古いか、アプリがこの API の使用許可リストに登録されていません。
  1. アプリが API の使用許可リストに登録されていることを確認します。
  2. Play ストア アプリを更新するようにユーザーに依頼します。
PLAY_STORE_NOT_FOUND デバイス上に公式の Google Play ストア アプリが見つかりませんでした。 適切なバージョンの Play ストアをインストールするようにユーザーに依頼します。
NETWORK_ERROR 利用可能なネットワークが見つかりませんでした。 ネットワーク接続を確認するようにユーザーに依頼します。
PLAY_STORE_ACCOUNT_NOT_FOUND デバイス上に Play ストア アカウントが見つかりませんでした。 Google Play ストアの認証を行うようにユーザーに依頼します。
APP_NOT_INSTALLED 呼び出し元アプリがインストールされていません。 なんらかの問題が発生しており、攻撃の可能性もあります。対応手順はありません。
PLAY_SERVICES_NOT_FOUND Play 開発者サービスを利用できないか、更新する必要があります。 Play 開発者サービスをインストールまたは更新するようにユーザーに依頼します。
APP_UID_MISMATCH 呼び出し元アプリの UID(ユーザー ID)が Package Manager の UID と一致しません。 なんらかの問題が発生しており、攻撃の可能性もあります。対応手順はありません。
TOO_MANY_REQUESTS 呼び出し元アプリからこの API へのリクエスト数が多すぎるため、スロットリングが行われています。 指数バックオフで再試行します。
CANNOT_BIND_TO_SERVICE Play ストアでサービスにバインドできませんでした。デバイスに古いバージョンの Play ストアがインストールされている可能性があります。 Play ストアを更新するようにユーザーに依頼します。
NONCE_TOO_SHORT ノンスの長さが短すぎます。ノンスは、16 バイト以上(Base64 エンコード前)でなければなりません。 ノンスを長くして再試行します。
NONCE_TOO_LONG ノンスの長さが長すぎます。ノンスは、500 バイト未満(Base64 エンコード前)でなければなりません。 ノンスを短くして再試行します。
GOOGLE_SERVER_UNAVAILABLE Google サーバーの不明な内部エラーです。 指数バックオフで再試行します。バグの報告を検討してください。
NONCE_IS_NOT_BASE64 ノンスが「Base64、ウェブセーフ、ラップなし」形式ではありません。 正しいノンス形式で再試行します。
INTERNAL_ERROR 不明な内部エラーです。 指数バックオフで再試行します。バグの報告を検討してください。

ネイティブ固有のエラーコード

ネイティブのエラーコードの前には、名称の競合を避けるため、INTEGRITY_ 接頭辞が付加されています。ネイティブ API には、上記のエラーコードに加えて、次のエラーコードがあります。

エラーコード 説明 対応手順
INTEGRITY_INITIALIZATION_NEEDED IntegrityManager が初期化されていません。 最初に IntegrityManager_init() を呼び出します。
INTEGRITY_INITIALIZATION_FAILED Integrity API の初期化中にエラーが発生しました。 指数バックオフで再試行します。バグの報告を検討してください。
INTEGRITY_INVALID_ARGUMENT Integrity API に無効な引数が渡されました。 正しい引数で再試行します。

インストール時整合性保護(EAP)

Play Integrity API の早期アクセス プログラムに参加するデベロッパーの方には、不正使用対策戦略をさらに強化するため、インストール時整合性保護の早期アクセス プログラムへの参加もおすすめします。インストール時保護を使用するには、インストール要件を提出します。それ以降、作業は必要ありません。インストール時保護と、Play Integrity API を使用したランタイム時のアプリ完全性チェックを組み合わせれば、アプリを強力に保護できます。

概要

インストール時整合性保護を使用すると、不明バージョンや未承認バージョンのアプリやゲームのインストールを減らすことができます。まず、アプリのパッケージ名に対してインストール要件を指定します。それ以降、そのパッケージ名のアプリが Android 11 以降搭載の GMS ライセンス付き Android デバイスにインストールされるたびに、指定したインストール要件のオフライン コピーに照らして Android プラットフォームがアプリを評価します。インストール元は問いません。要件を満たしていない場合は、インストール検証エラーが発生します。

インストール要件

インストール時保護を使用するには、アプリのパッケージ名で許可する署名証明書のリスト(テストを継続できるようデバッグ用証明書も含めたもの)を提供する必要があります。そのいずれかの署名証明書があるアプリは、インストールが正常に行われます。アプリの署名証明書がリストにない場合は、インストール検証エラーで失敗します。

必要に応じて、さらに 2 つのインストール要件を指定できます。

  • そのパッケージ名が adb を使用してインストールされないよう指定できます。
  • そのアプリが常にプリインストールで提供される(つまり、新たにインストールすることはできない)よう指定できます。

その他の注意事項

インストール時保護を使用する際は、次の点に注意してください。

  • インストール時整合性保護は、Google Play 開発者サービスと Android 11(API レベル 30)以上を搭載した GMS ライセンス付きの Android デバイス上で、あらゆるインストール元からのすべてのインストールをチェックします。改造やルート権限取得がなされた Android デバイス上では、インストール時保護はアプリ インストールを保護できません。
  • アプリへの使用を許可する署名証明書を、漏れなく提供してください。アプリが承認済みアプリストアから再署名された場合は、その署名証明書を忘れずに許可リストに含めてください。テストに関しては、許可する署名証明書として開発署名証明書を提出するか、内部アプリ共有を使用してテストビルドをインストールします(内部アプリ共有のインストールは許可されるため)。
  • インストール要件の新規作成、更新、または削除がデバイスに反映されるまでには時間がかかります。プッシュには最大 2 週間かかり、デバイスに受信されるタイミングは、デバイスの更新設定およびデータや Wi-Fi へのアクセス状況によって異なります。インストール要件を反映できなかった場合、Google Play 開発者サービスによって更新が複数回試行されます。
  • ユーザーがインストール要件により拒否されたアプリをインストールしようとすると、Android プラットフォームにより INSTALL_FAILED_VERIFICATION_FAILURE が生成されます。このエラーは、インストーラごとの UI で、あるいはサイドローディング中のエラー メッセージとして、解釈および表示されます。

インストール時保護をリクエストする

インストール時整合性保護の EAP に参加するには、こちらの Google フォームでインストール要件を送信してください。新規アプリの場合、すべてのバージョン番号にインストール時保護が適用されます。既存アプリの場合は、指定したバージョン番号から適用されます。

インストール時整合性保護の EAP からアプリを削除するには、Google パートナー マネージャーにお問い合わせください。なお、インストール要件の削除がデバイスに反映されるまでには時間がかかります。

その他の Play 完全性保護ツール

Play Integrity API に加え、以下の完全性保護ツールを不正使用対策戦略に使用することをおすすめします。

信頼できないデバイスを配信対象から除外する

完全性チェックに合格していないデバイスによる Google Play からのアプリのインストールを阻止できます。このオプションにより、不明なデバイスや信頼できないデバイスは Google Play からアプリをダウンロードできなくなります。ただし、APK ファイルにアクセスできるユーザーは、そこから直接アプリをインストールできます。

デバイス除外ルールを設定するには:

  1. Google Play Console の [デバイス カタログ] ページに移動します。
  2. [サポートされているデバイス] と表示されているプルダウンで、[除外されたデバイス] を選択します。
  3. 右上にある [除外ルールの管理] を選択します。
  4. [SafetyNet attestation API] の横で、次のいずれかを選択します。
    • 除外しない: デフォルトで選択されています。
    • 基本的な整合性を満たしていないデバイスを除外する: これは、Play Integrity API のレスポンスで MEETS_BASIC_INTEGRITY を返さないデバイスを除外することと同じです。
    • すべて除外: これは、Play Integrity API のレスポンスで MEETS_DEVICE_INTEGRITY を返さないデバイスを除外することと同じです。
  5. [適用] をクリックします。

Automatic Integrity Protection

Automatic Integrity Protection(AIP)は、ワンクリックで Play Integrity API の代わりに、完全にオフラインでの実行が可能なアプリやゲームを保護します。AIP を使用すると、Google Play はアプリのコードにランタイム チェックを追加して、改変や再配布を制限します。さらに、高度な難読化とリバース エンジニアリング対策の手法を使用して、これらのチェックを削除しにくくします。インストーラ チェックに失敗した場合、Google Play でアプリを入手するよう求めるメッセージがユーザーに表示されます。改変チェックに失敗した場合、アプリは実行されません。

AIP を不正使用対策戦略の一環として Play Integrity API と組み合わせると、攻撃者がアプリを再パッケージ化して再配布することが困難になります。詳しくは、Automatic Integrity Protection のガイドをご覧ください。

AIP の利用をリクエストするには、Google パートナー マネージャーにお問い合わせください。お使いのアカウントで利用可能になると、Google Play Console の [アプリの完全性] ページで、アプリの AIP をオン、オフできるようになります。

フィードバック

問題やフィードバックについては、integrity-api-eap@google.com までメールでご連絡ください。

統合ガイドの変更履歴

バージョン 説明 日付
2.0.0 08/17/21
1.1.0
  • 概要に、紹介動画へのリンクを追加しました。
  • ダウンロード フォルダにネイティブ API を追加しました。
  • API の使用制限に、Play とそれ以外で異なるパッケージ名を使用する場合に関する情報を追加しました。
  • デバイスの完全性フィールドに、仮想ラベルの例として PC 版 Android(AoPC)を追加しました。
07/14/21
1.0.1
  • ネイティブ API の詳細とエラーコードを追加しました。
07/06/21
1.0.0
  • 初期バージョン。
07/02/21