Сделайте стандартный запрос API

На этой странице описывается выполнение стандартных запросов API для вердиктов целостности, которые поддерживаются в Android 5.0 (уровень API 21) или выше. Вы можете сделать стандартный запрос API для вердикта целостности всякий раз, когда ваше приложение выполняет вызов сервера, чтобы проверить, является ли взаимодействие подлинным.

Обзор

Диаграмма последовательности, показывающая высокоуровневую структуру API Play Integrity.

Стандартный запрос состоит из двух частей:

  • Подготовьте поставщика токенов целостности (один раз) . Вам необходимо вызвать API целостности, чтобы подготовить поставщика токенов целостности задолго до того, как вам понадобится получить вердикт целостности. Например, вы можете сделать это при запуске приложения или в фоновом режиме до того, как потребуется вердикт целостности.
  • Запрос токена целостности (по требованию) : всякий раз, когда ваше приложение отправляет запрос к серверу, подлинность которого вы хотите проверить, вы запрашиваете токен целостности и отправляете его на внутренний сервер вашего приложения для расшифровки и проверки. Затем ваш внутренний сервер сможет решить, как действовать.

Подготовьте поставщика токенов целостности (один):

  1. Ваше приложение обращается к поставщику токенов целостности, сообщая номер вашего проекта Google Cloud.
  2. Ваше приложение хранит поставщика токенов целостности в памяти для дальнейших вызовов проверки аттестации.

Запрос токена целостности (по требованию):

  1. Для действия пользователя, которое необходимо защитить, ваше приложение вычисляет хэш (используя любой подходящий алгоритм хэширования, например SHA256) запроса, который необходимо сделать.
  2. Ваше приложение запрашивает токен целостности, передавая хеш запроса.
  3. Ваше приложение получает подписанный и зашифрованный токен целостности от Play Integrity API.
  4. Ваше приложение передает токен целостности в серверную часть вашего приложения.
  5. Серверная часть вашего приложения отправляет токен на сервер Google Play. Сервер Google Play расшифровывает и проверяет вердикт, возвращая результаты на серверную часть вашего приложения.
  6. Серверная часть вашего приложения решает, как действовать дальше, на основе сигналов, содержащихся в полезных данных токена.
  7. Серверная часть вашего приложения отправляет результаты решения в ваше приложение.

Подготовьте поставщика токенов целостности (один).

Прежде чем сделать стандартный запрос вердикта целостности из Google Play, необходимо подготовить (или «прогреть») поставщика токенов целостности. Это позволяет Google Play разумно кэшировать информацию о частичной аттестации на устройстве, чтобы уменьшить задержку на критическом пути, когда вы делаете запрос на вердикт целостности. Повторная подготовка поставщика токенов — это способ повторить проверки целостности с меньшими затратами ресурсов, что сделает следующий запрошенный вами вердикт целостности более актуальным.

Вы можете подготовить поставщика токенов целостности:

  • При запуске вашего приложения (т. е. при холодном запуске). Подготовка поставщика токенов является асинхронной и поэтому не влияет на время запуска. Этот вариант будет хорошо работать, если вы планируете сделать запрос вердикта целостности вскоре после запуска приложения, например, когда пользователь входит в систему или игрок присоединяется к игре.
  • Когда ваше приложение открыто (т. е. при теплом запуске). Однако обратите внимание, что каждый экземпляр приложения может подготовить токен целостности не более 5 раз в минуту.
  • В любое время в фоновом режиме, когда вы хотите подготовить токен перед запросом вердикта целостности.

Чтобы подготовить поставщика токенов целостности, выполните следующие действия:

  1. Создайте StandardIntegrityManager , как показано в следующих примерах.
  2. Создайте PrepareIntegrityTokenRequest , предоставив номер проекта Google Cloud с помощью метода setCloudProjectNumber() .
  3. Используйте менеджер для вызова 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();
}

Родной

/// 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);

Защита запросов от несанкционированного доступа (рекомендуется)

Когда вы проверяете действие пользователя в своем приложении с помощью Play Integrity API, вы можете использовать поле requestHash для защиты от несанкционированных атак. Например, игра может захотеть сообщить о счете игрока на внутренний сервер игры, а ваш сервер хочет убедиться, что этот счет не был подделан прокси-сервером. Play Integrity API возвращает значение, которое вы установили в поле requestHash внутри подписанного ответа о целостности. Без requestHash токен целостности будет привязан только к устройству, а не к конкретному запросу, что открывает возможность атаки. Следующие инструкции описывают, как эффективно использовать поле requestHash :

Когда вы запрашиваете вердикт целостности:

  • Вычислите дайджест всех соответствующих параметров запроса (например, SHA256 стабильной сериализации запроса) на основе происходящего действия пользователя или запроса сервера. Значение, установленное в поле requestHash , имеет максимальную длину 500 байт. Включите в requestHash любые данные запроса приложения, которые имеют решающее значение или имеют отношение к действию, которое вы проверяете или защищаете. Поле requestHash дословно включено в токен целостности, поэтому длинные значения могут увеличить размер запроса.
  • Предоставьте дайджест в качестве поля requestHash в Play Integrity API и получите токен целостности.

Когда вы получаете вердикт целостности:

  • Декодируйте токен целостности и извлеките поле requestHash .
  • Вычислите дайджест запроса так же, как в приложении (например, SHA256 стабильной сериализации запроса).
  • Сравните дайджесты на стороне приложения и на стороне сервера. Если они не совпадают, запрос не заслуживает доверия.

Запрос вердикта о целостности (по требованию)

После того как вы подготовили поставщика токенов целостности, вы можете начать запрашивать вердикты целостности из Google Play. Для этого выполните следующие шаги:

  1. Получите StandardIntegrityTokenProvider , как показано выше.
  2. Создайте StandardIntegrityTokenRequest , предоставив хеш запроса действия пользователя, которое вы хотите защитить, с помощью метода setRequestHash .
  3. Используйте поставщика токенов целостности для вызова 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();
}

Родной

/// 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();

Расшифровать и проверить вердикт целостности

После того как вы запросите вердикт целостности, Play Integrity API предоставляет зашифрованный токен ответа. Чтобы получить вердикт целостности устройства, вам необходимо расшифровать токен целостности на серверах Google. Для этого выполните следующие действия:

  1. Создайте сервисную учетную запись в проекте Google Cloud, связанную с вашим приложением.
  2. На сервере вашего приложения получите токен доступа из учетных данных вашей учетной записи службы, используя область playintegrity, и выполните следующий запрос:

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
    '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. Прочтите ответ в формате JSON.

Полученная полезная нагрузка представляет собой текстовый токен, содержащий вердикты целостности .

Автоматическая защита от повтора

Чтобы смягчить атаки повторного воспроизведения, Google Play автоматически гарантирует, что каждый токен целостности не может быть использован повторно много раз. Попытка повторно расшифровать один и тот же токен приведет к пустым вердиктам.