PC 版 Play Integrity をアプリに統合する

PC 版 Play Integrity を使用すると、ゲーム イベントとサーバー リクエストが正規の PC デバイス上の PC 版 Google Play Games の正規のインスタンスから送信されたものかどうかを確認できます。潜在的に危険なデバイスや不明なエミュレータを検出することで、ゲームのバックエンド サーバーで、不正行為、不正アクセス、不正なトラフィック、不正使用を防ぐための適切な措置を講じることができます。

前提条件

ステップ 1: ゲームで PC 版 Play Integrity を使用する方法を決定する

PC 版 Play Integrity を呼び出して環境の完全性判定の結果を取得するタイミングを決定します。たとえば、ゲームが開かれたとき、プレーヤーがログインしたとき、プレーヤーがマルチプレーヤー ゲームに参加したときに判定をリクエストできます。次に、さまざまな完全性レスポンスを処理する方法を決定します。たとえば、次のような操作を行えます。

  • 強制措置を講じずに回答を収集し、データを内部で分析して、不正行為の有用なシグナルかどうかを判断します。
  • レスポンスを収集し、バックエンド サーバーにロジックを実装して、完全性判定に合格したデバイスではゲームを通常どおりプレイできるようにし、疑わしい環境からのトラフィックに対してはアクセスを拒否または制限します。
  • レスポンスを収集し、完全性チェックに合格したデバイスのプレーヤーをマッチングするロジックと、疑わしい環境からのトラフィックをマッチングするロジックをバックエンドに実装します。

ステップ 2: ゲームで完全性トークンをリクエストする

PC 版 Play Integrity をウォームアップする

PC 向け Play Integrity を準備(「ウォームアップ」)します。これにより、完全性判定の結果がリクエストされたときに、Google Play はクリティカル パスのレイテンシを短縮するためにデバイス上の部分的な証明書情報のスマート キャッシングを行うことができます。ゲームが開いたらすぐに非同期でこれを行うことで、必要に応じてオンデマンドの完全性リクエストを行うことができます。

void PrepareIntegrityToken(
  const PrepareIntegrityTokenParams & params,
  PrepareIntegrityTokenContinuation continuation
)

成功すると、継続は、完全性トークンのリクエストに使用される RequestTokenData を含む PrepareIntegrityTokenResultValue で呼び出されます。このデータはメモリ内にキャッシュに保存され、RequestIntegrityToken の呼び出しのために、アプリのセッション期間中再利用されます。

完全性判定の結果を完全に再評価する必要があるとアプリケーションが判断した場合にのみ、PrepareIntegrityToken の呼び出しを行う必要があります。

詳細
パラメータ params: Google Cloud プロジェクト番号を含むパラメータ。
continuation: 完全性トークン プロバイダを返す非同期コールバック。

PrepareIntegrityToken アクションの呼び出し方法を示すコード スニペットは次のとおりです。

google::play::integrity::IntegrityClient client_;

google::play::integrity::PrepareIntegrityTokenResult
IntegrityInterface::PrepareIntegrityToken(int64_t cloud_project_number) {
  google::play::integrity::PrepareIntegrityTokenParams params;
  params.cloud_project_number = cloud_project_number;

  auto promise = std::make_shared<
      std::promise<google::play::integrity::PrepareIntegrityTokenResult>>();
  client_.PrepareIntegrityToken(
      params,
      [promise](
          google::play::integrity::PrepareIntegrityTokenResult result) {
        promise->set_value(std::move(result));
      });

  return promise->get_future().get();
}

完全性トークンをリクエストする

完全性トークンは、デバイスが改ざんされていないことをゲームで確認するためのメカニズムです。真正なものかどうかを確認したいサーバー リクエストをゲームが実行するたびに、完全性トークンをリクエストして、複合と検証を行うためにゲームのバックエンド サーバーに送信できます。

PC 向け Play Integrity API を使用してアプリ内のユーザー アクションをチェックする場合は、RequestIntegrityTokenParams::request_hash フィールドを使用して改ざん攻撃を軽減できます。たとえば、プレーヤーのスコアをゲームのバックエンド サーバーにレポートするゲームで、プロキシ サーバーによってスコアが改ざんされていないことを確認したい場合があります。PC 向け Play Integrity は、このフィールドに設定した値を、署名付きの完全性レスポンス内で返すことができます。requestHash がなければ、完全性トークンはデバイスにのみバインドされ、特定のリクエストにはバインドされないため、攻撃を受ける可能性が生じます。

void RequestIntegrityToken(
  const RequestIntegrityTokenParams & params,
  RequestIntegrityTokenContinuation continuation
)

攻撃の可能性を軽減するため、完全性判定の結果をリクエストするときは、次の点に注意してください。

  • 実行中のユーザー アクションまたはサーバー リクエストから、すべての関連するリクエスト パラメータ(安定したリクエストのシリアル化の SHA256 など)のダイジェストを計算します。
  • RequestIntegrityTokenParams::request_hash フィールドをダイジェストに設定します。
詳細
パラメータ params: 準備された RequestTokenData と完全性チェック リクエスト ハッシュを含むパラメータ。
continuation: データを返す非同期コールバック。

RequestIntegrityToken アクションを呼び出す方法を示すコード スニペットは次のとおりです。

absl::StatusOr<google::play::integrity::RequestIntegrityTokenResult>
IntegrityInterface::RequestIntegrityToken(
    const google::play::integrity::PrepareIntegrityTokenResult&
        prepare_integrity_token_result,
    const std::string& request_hash) {
  // Check if the prepare_integrity_token_result is OK
  if (!prepare_integrity_token_result.ok()) {
    return absl::FailedPreconditionError(
        absl::StrCat("PrepareIntegrityTokenResult is not OK. Error code: ",
                     prepare_integrity_token_result.error_code));
  }

  google::play::integrity::RequestIntegrityTokenParams params{
      .request_token_data =
          prepare_integrity_token_result.request_token_data,
      .request_hash = request_hash};

  auto promise = std::make_shared<std::promise<
      google::play::integrity::RequestIntegrityTokenResult>>();
  client_.RequestIntegrityToken(
      params,
      [promise](google::play::integrity::RequestIntegrityTokenResult result) {
        promise->set_value(std::move(result));
      });

  return promise->get_future().get();
}

ステップ 3: 次にゲームのバックエンド サーバーで完全性トークンを復号して検証する

完全性トークンを復号する

完全性判定の結果をリクエストすると、暗号化されたレスポンス トークンが Play Integrity API によって提供されます。デバイスの完全性判定の結果を取得するには、Google のサーバーで完全性トークンを復号する必要があります。

  1. アプリにリンクされている Google Cloud プロジェクト内にサービス アカウントを作成します。
  2. アプリのサーバーで、playintegrity スコープを使用してサービス アカウントの認証情報からアクセス トークンを取得し、次のリクエストを実行します。

    playintegrity.googleapis.com/v1/<var>PACKAGE_NAME</var>:decodePcIntegrityToken -d \
     '{ "integrity_token": "<var>INTEGRITY_TOKEN</var>" }'
    
  3. JSON レスポンスを読み取ります。

結果として返されるペイロードは、デベロッパー提供の情報とともに完全性判定の結果と詳細を含む書式なしテキストのトークンです。復号された完全性トークンは次のようになります。

{
  "requestDetails": {
    "requestPackageName": "com.your.package.name",
    "requestTime": "2025-08-29T13:10:37.285Z",
    "requestHash": "your_request_hash_string"
  },
  "deviceIntegrity": {
    "deviceRecognitionVerdict": [
      "MEETS_PC_INTEGRITY"
    ]
  },
}

完全性トークンを検証する

デコードされた完全性トークンの requestDetails フィールドには、リクエストに関する情報が格納されています。この情報にはデベロッパー提供情報が含まれ、requestHash にあります。

requestHash フィールドと packageName フィールドは、元のリクエストのフィールドと一致している必要があります。したがって、次のコード スニペットに示すように、requestPackageNamerequestHash が元のリクエストで送信されたものと一致することを確認して、JSON ペイロードの requestDetails の部分を検証します。

const auto& request_details = json_payload["requestDetails"];

if (request_details.value("requestPackageName", "") != <YOUR_PACKAGE_NAME>) {
  // Don't trust the verdicts.
}

// Check for the existence of the request_hash.
// If you set a request hash in the request and it's not present, you shouldn't
// trust the verdicts.
if (!request_details.contains("requestHash")) {
    // Don't trust the verdicts.
}


// The requestHash from request_details needs to match the request hash your
// app provided.
if (request_details.value("requestHash", "") != <PROVIDED_REQUEST_HASH>) {
    // Don't trust the verdicts.
}

// You can read the rest of payload's fields.

ステップ 4: 完全性判定の結果に基づいて実行するアクションを決定する

deviceIntegrity フィールドには、単一の値 deviceRecognitionVerdict が含まれる可能性があります。この値を使用して、ゲームが Play Integrity チェックに合格した PC で実行されているかどうかを判断できます(これは MEETS_PC_INTEGRITY レスポンスです)。ゲームのバックエンド サーバーは、この情報を収集し、ゲームイベントの続行を許可するか、リスクの高いトラフィックへのアクセスを拒否するかなど、ゲームが取るべきアクションを決定するために使用できます。

"deviceIntegrity": {
  "deviceRecognitionVerdict": ["MEETS_PC_INTEGRITY"]
}

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

MEETS_PC_INTEGRITY
このゲームは、オンデバイスの改ざんが検出されなかった正規の PC 環境で実行されています。
空(空の値)
ゲームは、API フックなどの攻撃やシステム侵害(改ざんされた Google デスクトップ サービス バージョンを実行しているデバイスなど)の兆候があるデバイス、または Google Play の完全性チェックに合格していない仮想デバイス(エミュレータなど)で動作しています。

ステップ 5: エラーコードを処理する

ゲームが PC 向け Play Integrity のリクエストを行い、呼び出しに失敗すると、ゲームはエラーコードを受け取ります。これらのエラーは、ネットワーク接続の不良などの環境に関する問題、API 統合の問題、悪意のあるアクティビティやアクティブな攻撃など、さまざまな理由で発生する可能性があります。

再試行可能なエラーコード

以下のエラーの原因は一時的な事象であることがあるため、指数バックオフ戦略で呼び出しを再試行する必要があります。

IntegrityError エラーの説明 エラーコード
kNetworkError デバイスのネットワーク接続に関する問題。 5
kTooManyRequests デバイスからのリクエストが多すぎます。 6
kClientTransientError クライアントの一時的な問題。 7

再試行戦略に関するその他の推奨事項については、こちらをご覧ください。

再試行できないエラーコード

以下のようなケースでは、自動再試行を使用できない場合があります。ただし、問題の原因となった事象に対処することで、手動での再試行は可能です。

IntegrityError エラーの説明 エラーコード 推奨される対処方法
kError SDK の動作中に致命的なエラーが発生しました。 1 再試行する前に、API の実装を確認してください。
kCloudProjectNumberIsInvalid Cloud プロジェクト番号が無効です。 2 Google Cloud コンソールで Cloud プロジェクト番号が正しく構成されていること、リクエストが正しい Cloud プロジェクト番号で行われていることを確認します。
kRequestHashTooLong リクエスト ハッシュが長すぎます。 3 生成されたリクエスト ハッシュが長すぎます。500 文字未満にしてください。
kNoValidPreparedTokenFound トークン リクエストを行う前に準備されたトークンはありません。 4 [RequestIntegrityToken][request-integrity-token] 呼び出しを行う前に、[PrepareIntegrityToken][prepare-token] アクションを呼び出します。
kSdkRuntimeUpdateRequired Play for Native SDK の更新が必要です。 8 デバイスの Google Play 開発者サービス クライアントが最新の状態であり、Play for Native PC SDK の最新バージョンを使用していることを確認します。