На этой странице описывается выполнение стандартных запросов API для вердиктов целостности, которые поддерживаются на Android 5.0 (уровень API 21) или выше. Вы можете выполнить стандартный запрос API для вердикта целостности всякий раз, когда ваше приложение делает вызов сервера, чтобы проверить, является ли взаимодействие подлинным.
Обзор
Стандартный запрос состоит из двух частей:
- Подготовка поставщика маркера целостности (однократно) : Вам необходимо вызвать Integrity API, чтобы подготовить поставщика маркера целостности задолго до того, как вам понадобится получить вердикт целостности. Например, вы можете сделать это при запуске приложения или в фоновом режиме, прежде чем потребуется вердикт целостности.
- Запросить токен целостности (по требованию) : всякий раз, когда ваше приложение делает запрос к серверу, подлинность которого вы хотите проверить, вы запрашиваете токен целостности и отправляете его на внутренний сервер вашего приложения для расшифровки и проверки. Затем ваш внутренний сервер может решить, как действовать.
Подготовьте поставщика токена целостности (единоразово):
- Ваше приложение обращается к поставщику токенов целостности, используя номер вашего проекта Google Cloud.
- Ваше приложение хранит поставщик маркера целостности в памяти для дальнейших вызовов проверки аттестации.
Запросить токен целостности (по требованию):
- Для действия пользователя, которое необходимо защитить, ваше приложение вычисляет хэш (используя любой подходящий алгоритм хэширования, например SHA256) запроса, который необходимо выполнить.
- Ваше приложение запрашивает токен целостности, передавая хэш запроса.
- Ваше приложение получает подписанный и зашифрованный токен целостности от API Play Integrity.
- Ваше приложение передает токен целостности в бэкэнд вашего приложения.
- Бэкенд вашего приложения отправляет токен на сервер Google Play. Сервер Google Play расшифровывает и проверяет вердикт, возвращая результаты на бэкенд вашего приложения.
- Бэкэнд вашего приложения решает, как действовать дальше, на основе сигналов, содержащихся в полезной нагрузке токена.
- Бэкэнд вашего приложения отправляет результаты решения в ваше приложение.
Подготовьте поставщика маркера целостности (единоразово)
Прежде чем сделать стандартный запрос на вердикт целостности из Google Play, необходимо подготовить (или «разогреть») поставщика токенов целостности. Это позволяет Google Play разумно кэшировать частичную информацию об аттестации на устройстве, чтобы уменьшить задержку на критическом пути, когда вы делаете запрос на вердикт целостности. Повторная подготовка поставщика токенов — это способ повторить менее ресурсоемкие проверки целостности, что сделает следующий запрашиваемый вами вердикт целостности более актуальным.
Вы можете подготовить поставщика токена целостности:
- При запуске вашего приложения (т. е. при холодном запуске). Подготовка поставщика токенов происходит асинхронно и не повлияет на время запуска. Этот вариант подойдет, если вы планируете сделать запрос на вердикт целостности вскоре после запуска приложения, например, когда пользователь входит в систему или игрок присоединяется к игре.
- Когда ваше приложение открыто (т.е. при теплом запуске). Однако учтите, что каждый экземпляр приложения может подготовить токен целостности только до 5 раз в минуту.
- В любой момент в фоновом режиме, когда вам нужно подготовить токен перед запросом на вынесение вердикта о целостности.
Чтобы подготовить поставщика токена целостности, выполните следующие действия:
- Создайте
StandardIntegrityManager
, как показано в следующих примерах. - Создайте
PrepareIntegrityTokenRequest
, указав номер проекта Google Cloud с помощью методаsetCloudProjectNumber()
. - Используйте менеджер для вызова
prepareIntegrityToken()
, предоставляяPrepareIntegrityTokenRequest
.
Ява
import com.google.android.gms.tasks.Task; // Create an instance of a manager. StandardIntegrityManager standardIntegrityManager = IntegrityManagerFactory.createStandard(applicationContext); StandardIntegrityTokenProvider integrityTokenProvider; long cloudProjectNumber = ...; // Prepare integrity token. Can be called once in a while to keep internal // state fresh. standardIntegrityManager.prepareIntegrityToken( PrepareIntegrityTokenRequest.builder() .setCloudProjectNumber(cloudProjectNumber) .build()) .addOnSuccessListener(tokenProvider -> { integrityTokenProvider = tokenProvider; }) .addOnFailureListener(exception -> handleError(exception));
Единство
IEnumerator PrepareIntegrityTokenCoroutine() { long cloudProjectNumber = ...; // Create an instance of a standard integrity manager. var standardIntegrityManager = new StandardIntegrityManager(); // Request the token provider. var integrityTokenProviderOperation = standardIntegrityManager.PrepareIntegrityToken( new PrepareIntegrityTokenRequest(cloudProjectNumber)); // Wait for PlayAsyncOperation to complete. yield return integrityTokenProviderOperation; // Check the resulting error code. if (integrityTokenProviderOperation.Error != StandardIntegrityErrorCode.NoError) { AppendStatusLog("StandardIntegrityAsyncOperation failed with error: " + integrityTokenProviderOperation.Error); yield break; } // Get the response. var integrityTokenProvider = integrityTokenProviderOperation.GetResult(); }
Нереальный движок
// .h void MyClass::OnPrepareIntegrityTokenCompleted( EStandardIntegrityErrorCode ErrorCode, UStandardIntegrityTokenProvider* Provider) { // Check the resulting error code. if (ErrorCode == EStandardIntegrityErrorCode::StandardIntegrity_NO_ERROR) { // ... } } // .cpp void MyClass::PrepareIntegrityToken() { int64 CloudProjectNumber = ... // Create the Integrity Token Request. FPrepareIntegrityTokenRequest Request = { CloudProjectNumber }; // Create a delegate to bind the callback function. FPrepareIntegrityOperationCompletedDelegate Delegate; // Bind the completion handler (OnPrepareIntegrityTokenCompleted) to the delegate. Delegate.BindDynamic(this, &MyClass::OnPrepareIntegrityTokenCompleted); // Initiate the prepare integrity token operation, passing the delegate to handle the result. GetGameInstance() ->GetSubsystem<UStandardIntegrityManager>() ->PrepareIntegrityToken(Request, Delegate); }
Родной
/// Initialize StandardIntegrityManager StandardIntegrityManager_init(/* app's java vm */, /* an android context */); /// Create a PrepareIntegrityTokenRequest opaque object. int64_t cloudProjectNumber = ...; PrepareIntegrityTokenRequest* tokenProviderRequest; PrepareIntegrityTokenRequest_create(&tokenProviderRequest); PrepareIntegrityTokenRequest_setCloudProjectNumber(tokenProviderRequest, cloudProjectNumber); /// Prepare a StandardIntegrityTokenProvider opaque type pointer and call /// StandardIntegrityManager_prepareIntegrityToken(). StandardIntegrityTokenProvider* tokenProvider; StandardIntegrityErrorCode error_code = StandardIntegrityManager_prepareIntegrityToken(tokenProviderRequest, &tokenProvider); /// ... /// Proceed to polling iff error_code == STANDARD_INTEGRITY_NO_ERROR if (error_code != STANDARD_INTEGRITY_NO_ERROR) { /// Remember to call the *_destroy() functions. return; } /// ... /// Use polling to wait for the async operation to complete. IntegrityResponseStatus token_provider_status; /// Check for error codes. StandardIntegrityErrorCode error_code = StandardIntegrityTokenProvider_getStatus(tokenProvider, &token_provider_status); if (error_code == STANDARD_INTEGRITY_NO_ERROR && token_provider_status == INTEGRITY_RESPONSE_COMPLETED) { /// continue to request token from the token provider } /// ... /// Remember to free up resources. PrepareIntegrityTokenRequest_destroy(tokenProviderRequest);
Защитить запросы от несанкционированного доступа (рекомендуется)
Когда вы проверяете действие пользователя в своем приложении с помощью API Play Integrity, вы можете использовать поле requestHash
для смягчения атак с целью подделки. Например, игра может захотеть сообщить счет игрока на внутренний сервер игры, и ваш сервер хочет убедиться, что этот счет не был подделан прокси-сервером. API Play Integrity возвращает значение, которое вы установили в поле requestHash
, внутри подписанного ответа целостности. Без requestHash
токен целостности будет привязан только к устройству, но не к конкретному запросу, что открывает возможность атаки. Следующие инструкции описывают, как эффективно использовать поле requestHash
:
Когда вы запрашиваете вердикт о честности:
- Вычислите дайджест всех соответствующих параметров запроса (например, SHA256 стабильной сериализации запроса) из действия пользователя или запроса сервера, который происходит. Значение, заданное в поле
requestHash
, имеет максимальную длину 500 байт. Включите вrequestHash
любые данные запроса приложения, которые имеют решающее значение или имеют отношение к действию, которое вы проверяете или защищаете. ПолеrequestHash
включено в токен целостности дословно, поэтому длинные значения могут увеличить размер запроса. - Предоставьте дайджест в качестве поля
requestHash
для API Play Integrity и получите токен целостности.
Когда вы получаете вердикт о добросовестности:
- Расшифруйте токен целостности и извлеките поле
requestHash
. - Вычислите дайджест запроса таким же образом, как в приложении (например, SHA256 стабильной сериализации запроса).
- Сравните дайджесты на стороне приложения и на стороне сервера. Если они не совпадают, запрос не заслуживает доверия.
Запросить вердикт о добросовестности (по требованию)
После того, как вы подготовили поставщика маркера целостности, вы можете начать запрашивать вердикты целостности из Google Play. Для этого выполните следующие шаги:
- Получите
StandardIntegrityTokenProvider
. - Создайте
StandardIntegrityTokenRequest
, указав хэш запроса действия пользователя, которое вы хотите защитить, с помощью методаsetRequestHash
. - Используйте поставщик токена целостности для вызова
request()
, предоставляяStandardIntegrityTokenRequest
.
Ява
import com.google.android.gms.tasks.Task; StandardIntegrityTokenProvider integrityTokenProvider; // See above how to prepare integrityTokenProvider. // Request integrity token by providing a user action request hash. Can be called // several times for different user actions. String requestHash = "2cp24z..."; Task<StandardIntegrityToken> integrityTokenResponse = integrityTokenProvider.request( StandardIntegrityTokenRequest.builder() .setRequestHash(requestHash) .build()); integrityTokenResponse .addOnSuccessListener(response -> sendToServer(response.token())) .addOnFailureListener(exception -> handleError(exception));
Единство
IEnumerator RequestIntegrityTokenCoroutine() { StandardIntegrityTokenProvider integrityTokenProvider; // See above how to prepare integrityTokenProvider. // Request integrity token by providing a user action request hash. Can be called // several times for different user actions. String requestHash = "2cp24z..."; var integrityTokenOperation = integrityTokenProvider.Request( new StandardIntegrityTokenRequest(requestHash) ); // Wait for PlayAsyncOperation to complete. yield return integrityTokenOperation; // Check the resulting error code. if (integrityTokenOperation.Error != StandardIntegrityErrorCode.NoError) { AppendStatusLog("StandardIntegrityAsyncOperation failed with error: " + integrityTokenOperation.Error); yield break; } // Get the response. var integrityToken = integrityTokenOperation.GetResult(); }
Нереальный движок
// .h void MyClass::OnRequestIntegrityTokenCompleted( EStandardIntegrityErrorCode ErrorCode, UStandardIntegrityToken* Response) { // Check the resulting error code. if (ErrorCode == EStandardIntegrityErrorCode::StandardIntegrity_NO_ERROR) { // Get the token. FString Token = Response->Token; } } // .cpp void MyClass::RequestIntegrityToken() { UStandardIntegrityTokenProvider* Provider = ... // Prepare the UStandardIntegrityTokenProvider. // Request integrity token by providing a user action request hash. Can be called // several times for different user actions. FString RequestHash = ...; FStandardIntegrityTokenRequest Request = { RequestHash }; // Create a delegate to bind the callback function. FStandardIntegrityOperationCompletedDelegate Delegate; // Bind the completion handler (OnRequestIntegrityTokenCompleted) to the delegate. Delegate.BindDynamic(this, &MyClass::OnRequestIntegrityTokenCompleted); // Initiate the standard integrity token request, passing the delegate to handle the result. Provider->Request(Request, Delegate); }
Родной
/// Create a StandardIntegrityTokenRequest opaque object. const char* requestHash = ...; StandardIntegrityTokenRequest* tokenRequest; StandardIntegrityTokenRequest_create(&tokenRequest); StandardIntegrityTokenRequest_setRequestHash(tokenRequest, requestHash); /// Prepare a StandardIntegrityToken opaque type pointer and call /// StandardIntegrityTokenProvider_request(). Can be called several times for /// different user actions. See above how to prepare token provider. StandardIntegrityToken* token; StandardIntegrityErrorCode error_code = StandardIntegrityTokenProvider_request(tokenProvider, tokenRequest, &token); /// ... /// Proceed to polling iff error_code == STANDARD_INTEGRITY_NO_ERROR if (error_code != STANDARD_INTEGRITY_NO_ERROR) { /// Remember to call the *_destroy() functions. return; } /// ... /// Use polling to wait for the async operation to complete. IntegrityResponseStatus token_status; /// Check for error codes. StandardIntegrityErrorCode error_code = StandardIntegrityToken_getStatus(token, &token_status); if (error_code == STANDARD_INTEGRITY_NO_ERROR && token_status == INTEGRITY_RESPONSE_COMPLETED) { const char* integrityToken = StandardIntegrityToken_getToken(token); } /// ... /// Remember to free up resources. StandardIntegrityTokenRequest_destroy(tokenRequest); StandardIntegrityToken_destroy(token); StandardIntegrityTokenProvider_destroy(tokenProvider); StandardIntegrityManager_destroy();
Если ваше приложение использует одного и того же поставщика токенов слишком долго, срок действия поставщика токенов может истечь, что приведет к ошибке INTEGRITY_TOKEN_PROVIDER_INVALID при следующем запросе токена. Вам следует обработать эту ошибку, запросив нового поставщика.
Расшифруйте и проверьте вердикт целостности
После запроса вердикта целостности API Play Integrity предоставляет зашифрованный токен ответа. Чтобы получить вердикты целостности устройства, необходимо расшифровать токен целостности на серверах Google. Для этого выполните следующие действия:
- Создайте учетную запись службы в проекте Google Cloud, связанную с вашим приложением.
На сервере вашего приложения извлеките токен доступа из учетных данных вашей учетной записи службы, используя область действия playintegrity, и выполните следующий запрос:
playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \ '{ "integrity_token": "INTEGRITY_TOKEN" }'
Прочитайте ответ JSON.
Полученная полезная нагрузка представляет собой простой текстовый токен, содержащий вердикты о целостности .
Автоматическая защита от повторного воспроизведения
Чтобы смягчить атаки повторного воспроизведения, Google Play автоматически предотвращает многократное повторное использование токенов целостности. Попытка многократно расшифровать один и тот же токен приведет к очищенным вердиктам следующим образом:
- Результат распознавания устройства будет пустым.
- Вердикт о признании приложения и вердикт о лицензировании приложения будут установлены на
UNEVALUATED
. - Любой из дополнительных вердиктов, включенных с помощью Play Console, будет установлен на значение
UNEVALUATED
(или на пустой вердикт, если это вердикт с несколькими значениями).