Tworzenie standardowego żądania do interfejsu API

Na tej stronie opisujemy wykonywanie standardowych żądań do interfejsu API w celu oceny integralności, które są obsługiwane na Androidzie 5.0 (poziom interfejsu API 21) lub nowszym. Możesz wysyłać do interfejsu API standardowe żądanie oceny integralności za każdym razem, gdy aplikacja wykonuje wywołanie serwera, aby sprawdzić, czy interakcja jest autentyczna.

Przegląd

Schemat sekwencji przedstawiający ogólny projekt interfejsu Play Integrity API.

Żądanie standardowe składa się z 2 części:

  • Przygotuj dostawcę tokena integralności (jednorazowo): musisz wywołać interfejs Integrity API, aby dobrze przygotować dostawcę tokena integralności, zanim trzeba będzie uzyskać ocenę integralności. Możesz to zrobić np. wtedy, gdy aplikacja uruchamia się lub działa w tle, zanim potrzebna jest ocena integralności.
  • Wysyłanie żądania tokena integralności (na żądanie): za każdym razem, gdy aplikacja wysyła żądanie serwera, które chcesz sprawdzić, które chcesz sprawdzić, wysyłasz żądanie tokena integralności i wysyłasz go na serwer backendu aplikacji w celu odszyfrowania i weryfikacji. Serwer backendu będzie mógł podjąć odpowiednie działania.

Przygotuj dostawcę tokena integralności (jeden raz):

  1. Aplikacja wywołuje dostawcę tokena integralności, podając numer projektu Google Cloud.
  2. Aplikacja przechowuje dostawcę tokena integralności w pamięci na potrzeby dalszych wywołań sprawdzania atestu.

Wysyłanie żądania tokena integralności (na żądanie):

  1. W przypadku działania użytkownika, które musi być chronione, aplikacja oblicza hasz (za pomocą odpowiedniego algorytmu szyfrowania, np. SHA256) wysłanego żądania.
  2. Aplikacja prosi o token integralności, przekazując hasz żądania.
  3. Twoja aplikacja otrzymuje podpisany i zaszyfrowany token integralności z interfejsu Play Integrity API.
  4. Aplikacja przekazuje token integralności do backendu aplikacji.
  5. Backend aplikacji wysyła token do serwera Google Play. Serwer Google Play odszyfrowuje i weryfikuje ocenę, zwracając wyniki do backendu aplikacji.
  6. Backend aplikacji decyduje, co zrobić dalej, na podstawie sygnałów zawartych w ładunku tokena.
  7. Backend aplikacji wysyła do niej wyniki decyzji.

Przygotowywanie dostawcy tokena integralności (jeden wyłączony)

Zanim prześlesz do Google Play standardową prośbę o ocenę integralności, musisz przygotować (czyli „przygotować”) dostawcę tokena integralności. Dzięki temu Google Play może w inteligentny sposób buforować częściowe informacje o atestach na urządzeniu, aby zmniejszyć czas oczekiwania na ścieżce krytycznej, gdy wysyłasz żądanie oceny integralności. Ponowne przygotowanie dostawcy tokena to sposób na powtarzanie testów integralności z mniejszą ilością zasobów, co pozwoli na kolejną ocenę integralności, której zażądasz.

Możesz przygotować dostawcę tokena integralności:

  • Po uruchomieniu aplikacji (np. przy uruchomieniu „na zimno”). Przygotowanie dostawcy tokena jest asynchroniczne, więc nie będzie miało wpływu na czas uruchamiania. Ta opcja sprawdzi się, jeśli planujesz wysłanie żądania oceny integralności tuż po uruchomieniu aplikacji, np. gdy użytkownik się loguje lub gracz dołącza do gry.
  • Kiedy aplikacja jest uruchomiona (np. podczas uruchamiania częściowo z pamięci). Pamiętaj jednak, że każda instancja aplikacji może przygotować token integralności tylko 5 razy na minutę.
  • w dowolnym momencie w tle, gdy chcesz przygotować token przed wysłaniem żądania oceny integralności.

Aby przygotować dostawcę tokena integralności, wykonaj te czynności:

  1. Utwórz StandardIntegrityManager w sposób opisany w przykładach poniżej.
  2. Zbuduj obiekt PrepareIntegrityTokenRequest, podając numer projektu Google Cloud za pomocą metody setCloudProjectNumber().
  3. Użyj menedżera, aby wywołać prepareIntegrityToken() i podać PrepareIntegrityTokenRequest.

Java

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

Jedność

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

Reklamy natywne

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

Chroń żądania przed manipulacją (zalecane)

Podczas sprawdzania działań użytkowników w aplikacji za pomocą interfejsu Play Integrity API możesz wykorzystać pole requestHash, aby zapobiegać atakom z manipulacją. Na przykład gra może chcieć zgłosić wynik gracza do serwera jej backendu, a Twój serwer chce się upewnić, że serwer proxy nie zmienił wyniku. Interfejs Play Integrity API zwraca wartość ustawioną w polu requestHash w podpisanej odpowiedzi dotyczącej integralności. Bez requestHash token integralności będzie powiązany tylko z urządzeniem, ale nie z konkretnym żądaniem, co stwarza ryzyko ataku. Te instrukcje opisują, jak skutecznie korzystać z pola requestHash:

Gdy poprosisz o ocenę integralności:

  • Oblicza podsumowanie wszystkich odpowiednich parametrów żądania (np. SHA256 stabilnej serializacji żądania) z występującego działania użytkownika lub żądania serwera. Wartość ustawiona w polu requestHash ma maksymalną długość 500 bajtów. requestHash musi zawierać wszystkie dane o żądaniach aplikacji, które są kluczowe lub związane z działaniem, które sprawdzasz lub zabezpieczasz. Pole requestHash jest zawarte w tokenie integralności w sposób dosłowny, więc długie wartości mogą zwiększać rozmiar żądania.
  • Podaj skrót jako pole requestHash do interfejsu Play Integrity API i uzyskaj token integralności.

Gdy otrzymasz ocenę integralności:

  • Zdekoduj token integralności i wyodrębnij pole requestHash.
  • Oblicz skrót żądania w taki sam sposób jak w aplikacji (np. za pomocą algorytmu SHA256 serializacji stabilnego żądania).
  • Porównaj skróty po stronie aplikacji i serwera. Jeśli żądania się nie zgadzają, żądanie nie jest wiarygodne.

Wysyłanie prośby o ocenę integralności (na żądanie)

Gdy przygotujesz dostawcę tokena integralności, możesz zacząć wysyłać prośby o oceny integralności do Google Play. Aby to zrobić:

  1. Uzyskaj StandardIntegrityTokenProvider w sposób opisany powyżej.
  2. Utwórz obiekt StandardIntegrityTokenRequest, podając hasz żądania działania użytkownika, które chcesz chronić za pomocą metody setRequestHash.
  3. Użyj dostawcy tokena integralności, aby wywołać request() i podać StandardIntegrityTokenRequest.

Java

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

Jedność

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

Reklamy natywne

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

Odszyfruj i zweryfikuj ocenę integralności

Gdy wyślesz żądanie oceny integralności, Play Integrity API dostarczy zaszyfrowany token odpowiedzi. Aby uzyskać oceny integralności urządzenia, musisz odszyfrować token integralności na serwerach Google. W tym celu wykonaj następujące czynności:

  1. Utwórz konto usługi w projekcie Google Cloud połączonym z Twoją aplikacją.
  2. Z serwera aplikacji pobierz token dostępu z danych logowania konta usługi, używając zakresu playintegrity, i wykonaj to żądanie:

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
    '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. Przeczytaj odpowiedź JSON.

Powstały w ten sposób ładunek jest zwykłym tokenem tekstowym, który zawiera oceny integralności.

Automatyczna ochrona przed ponownym odtwarzaniem

Aby łagodzić ataki typu „powtórka”, Google Play automatycznie dba o to, żeby żaden token integralności nie mógł być wielokrotnie używany. Próba wielokrotnego odszyfrowania tego samego tokena spowoduje, że wyniki będą puste.