Tworzenie standardowego żądania do interfejsu API

Na tej stronie opisujemy tworzenie standardowych żądań do interfejsu API na potrzeby oceny integralności, które są obsługiwane na Androidzie 5.0 (poziom API 21) i nowszych. Możesz przesłać standardowe żądanie do interfejsu API dotyczące oceny integralności za każdym razem, gdy aplikacja wysyła wywołanie serwera w celu sprawdzenia, czy interakcja jest autentyczna.

Przegląd

Diagram sekwencji pokazujący ogólny wygląd interfejsu Play Integrity API

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

  • Przygotuj dostawcę tokenów integralności (jednorazowo): zanim uzyskasz ocenę integralności, musisz wywołać interfejs Integrity API, aby przygotować dostawcę tokenów integralności. Możesz to zrobić na przykład podczas uruchamiania aplikacji lub w tle, zanim będzie potrzebna ocena integralności.
  • Żądanie tokena integralności (na żądanie): gdy Twoja aplikacja wysyła do serwera żądanie, które chcesz sprawdzić, jest autentyczne, żądasz tokena integralności i wysyłasz go na serwer backendu aplikacji w celu odszyfrowania i weryfikacji. Wtedy serwer backendu może zdecydować, co zrobić dalej.

Przygotuj dostawcę tokena integralności (wyłączone):

  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.

Poproś o token integralności (na żądanie):

  1. W przypadku działania użytkownika, które musi być chronione, aplikacja oblicza hasz żądania (za pomocą dowolnego odpowiedniego algorytmu szyfrowania, takiego jak SHA256).
  2. Aplikacja żąda tokena integralności, przekazując hasz żądania.
  3. 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 decyzję, zwracając wyniki do backendu aplikacji.
  6. Backend aplikacji określa dalsze kroki na podstawie sygnałów zawartych w ładunku tokena.
  7. Backend aplikacji wysyła do niej wyniki decyzji.

Przygotowywanie dostawcy tokena integralności (jednorazowo)

Zanim prześlesz do Google Play standardowe żądanie oceny integralności, musisz przygotować (lub „przygotować”) dostawcę tokenów integralności. Dzięki temu Google Play może inteligentnie zapisywać w pamięci podręcznej informacje o częściowych atestach, aby zmniejszyć opóźnienie na ścieżce krytycznej, gdy wysyłasz żądanie oceny integralności. Ponowne przygotowanie dostawcy tokenów to sposób na powtarzanie mniej intensywnych testów integralności używających zasobów. Dzięki temu kolejna ocena integralności, o którą prosisz, będzie bardziej aktualna.

Możesz przygotować dostawcę tokenów integralności:

  • Kiedy aplikacja się uruchamia (np. podczas uruchomienia „na zimno”). Przygotowywanie dostawcy tokenów odbywa się asynchronicznie, więc nie będzie miało wpływu na czas uruchamiania. Ta opcja sprawdzi się, jeśli planujesz wysyłać prośbę o ocenę integralności tuż po uruchomieniu aplikacji, na przykład gdy użytkownik się loguje lub dołącza do gry.
  • Gdy aplikacja jest otwarta (np. podczas uruchamiania częściowo z pamięci). Pamiętaj jednak, że każda instancja aplikacji może przygotować token integralności do 5 razy na minutę.
  • w dowolnym momencie w tle, gdy chcesz przygotować token z wyprzedzeniem, aby otrzymać ocenę integralności.

Aby przygotować dostawcę tokenów integralności, wykonaj te czynności:

  1. Utwórz StandardIntegrityManager, jak pokazano w poniższych przykładach.
  2. Utwórz PrepareIntegrityTokenRequest, podając numer projektu Google Cloud za pomocą metody setCloudProjectNumber().
  3. Poproś menedżera o skontaktowanie się z firmą prepareIntegrityToken(), podając 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);

Ochrona żądań przed manipulacją (zalecane)

Gdy sprawdzasz działanie użytkownika w aplikacji za pomocą interfejsu Play Integrity API, możesz korzystać z pola requestHash, aby zapobiegać atakom polegającym na manipulacji. Na przykład gra może zgłaszać wynik gracza do swojego backendu gry, a serwer chce się upewnić, że ten wynik nie został zmodyfikowany przez serwer proxy. 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 możliwość ataku. Poniższe instrukcje pokazują, jak skutecznie korzystać z pola requestHash:

Gdy prosisz o ocenę integralności:

  • Oblicza podsumowanie wszystkich istotnych parametrów żądania (np. SHA256 stabilnej serializacji żądań) z wykonywanego działania użytkownika lub żądania serwera. Wartość ustawiona w polu requestHash nie może przekraczać 500 bajtów. Uwzględnij w polu requestHash wszystkie dane dotyczące żądań aplikacji, które są kluczowe lub mają związek z sprawdzanym lub chronionym działaniem. Pole requestHash jest dosłownie uwzględnione w tokenie integralności, więc długie wartości mogą zwiększyć 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 podsumowanie żądania w taki sam sposób jak w aplikacji (np. z użyciem SHA256 szeregowania żądania).
  • Porównaj podsumowania po stronie aplikacji i po stronie serwera. Jeśli są inne, żądanie nie jest wiarygodne.

Poproś o ocenę integralności (na żądanie)

Po przygotowaniu dostawcy tokenów integralności możesz zacząć wysyłać do Google Play prośby o oceny integralności. Aby to zrobić:

  1. Uzyskaj StandardIntegrityTokenProvider, jak pokazano powyżej.
  2. Utwórz StandardIntegrityTokenRequest, podając za pomocą metody setRequestHash identyfikator żądania działania użytkownika, które chcesz chronić.
  3. Użyj dostawcy tokena integralności, aby wywołać request(), podając 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 sprawdź ocenę integralności

Gdy poprosisz o ocenę integralności, interfejs Play Integrity API udostępni zaszyfrowany token odpowiedzi. Aby uzyskać oceny integralności urządzenia, musisz odszyfrować token integralności na serwerach Google. Aby to zrobić:

  1. Utwórz konto usługi w projekcie Google Cloud połączonym z Twoją aplikacją. Podczas tego procesu musisz przypisać do konta usługi role Użytkownik konta usługi i Konsument korzystania z usług.
  2. Za pomocą zakresu Playintegrity pobierz token dostępu z danych logowania konta usługi na serwerze aplikacji i wyślij 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 to token tekstowy zawierający oceny integralności.

Automatyczna ochrona przed ponownym odtworzeniem

Aby ograniczyć ataki typu replay, Google Play automatycznie dba o to, aby nie można było wielokrotnie używać każdego tokena integralności. Wielokrotne próby odszyfrowania tego samego tokena spowodują uzyskanie pustych wyników.