На этой странице описывается выполнение стандартных запросов 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(); }
Unreal Engine
// .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(); }
Unreal Engine
// .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
(или на пустой вердикт, если это многозначный вердикт).
Устраните проблемы с вердиктом с помощью запроса Google Play (необязательно)
Получив заключение о целостности, ваш сервер может определить дальнейшие действия. Если заключение указывает на наличие проблемы, например, на отсутствие лицензии у приложения, его несанкционированный доступ или взлом устройства, вы можете предоставить пользователям возможность самостоятельно исправить проблему.
API Play Integrity предоставляет возможность отобразить диалоговое окно Google Play, предлагающее пользователю выполнить действие, например, загрузить официальную версию вашего приложения из Google Play.
Чтобы узнать, как вызывать эти диалоги из вашего приложения на основе ответа сервера, см. раздел Диалоги исправления .