Tworzenie klasycznego żądania do interfejsu API

Jeśli zamierzasz utworzyć standardowy interfejs API , które są odpowiednie dla większości użytkowników. dla programistów, możesz od razu przejść do integralności. . Na tej stronie dowiesz się, jak tworzyć klasyczne elementy Żądania do interfejsu API umożliwiające ocenę integralności, które są obsługiwane na Androidzie 4.4 na poziomie 19) lub wyższym.

co należy wziąć pod uwagę

Porównanie żądań standardowych i klasycznych

Możesz wysyłać żądania standardowe lub klasyczne albo ich kombinację w zależności od potrzeb aplikacji w zakresie bezpieczeństwa i zapobiegania nadużyciom. Żądania standardowe są jest odpowiedni dla wszystkich aplikacji i gier. Można go użyć do sprawdzenia, czy określone działanie lub wywołanie serwera jest autentyczne, a jednocześnie przekazuje pewne zabezpieczenia przed ponownym odtwarzaniem. i wydobycia do Google Play. Wykonywanie żądań klasycznych jest droższe, ponosisz odpowiedzialność za ich prawidłowe wdrożenie, a także wydobycia i określonych typów ataków. Klasyczne żądania powinny być mniej często niż standardowe prośby, na przykład jednorazowo w celu sprawdzenia czy działanie o dużej wartości lub o charakterze wrażliwym jest prawdziwe.

W tabeli poniżej znajdziesz najważniejsze różnice między tymi dwoma typami tagów żądania:

Standardowe żądanie do interfejsu API Klasyczne żądanie do interfejsu API
Wymagania wstępne
Wymagana minimalna wersja pakietu SDK na Androida Android 5.0 (poziom interfejsu API 21) lub nowszy Android 4.4 (poziom interfejsu API 19) lub nowszy
Wymagania Google Play Sklep Google Play i Usługi Google Play Sklep Google Play i Usługi Google Play
Szczegóły integracji
Wymagane rozgrzewka interfejsu API ✔️ (kilka sekund)
Typowe opóźnienie żądania Kilkaset milisekund Kilka sekund
Potencjalna częstotliwość żądań Częste (sprawdzanie na żądanie w przypadku działań i żądań) Rzadkie (jednorazowe sprawdzanie w przypadku działań o największej wartości lub najbardziej wrażliwych żądań)
tymczasowe zawieszenia użytkowników Większość rozgrzewek trwa krócej niż 10 s, ale wiąże się z wywołaniem serwera, dlatego zaleca się długi czas oczekiwania (np. 1 minutę). Żądania oceny są realizowane po stronie klienta Większość żądań ma wartość mniejszą niż 10 sekund, ale obejmują one wywołanie serwera, dlatego zalecamy długi limit czasu (np. 1 minutę)
Token oceny integralności
Zawiera informacje o urządzeniu, aplikacji i koncie
Buforowanie tokena Chroniona pamięć podręczna na urządzeniu dzięki Google Play Niezalecane
Odszyfrowywanie i weryfikację tokena przez serwer Google Play
Typowy czas oczekiwania na żądanie odszyfrowywania między serwerami 10 s milisekund z dostępnością „3-9s” 10 s milisekund z dostępnością „3-9s”
Odszyfruj i zweryfikuj token lokalnie w bezpiecznym środowisku serwera
Odszyfrowywanie i weryfikacja tokena po stronie klienta
Aktualność oceny integralności Niektóre automatyczne buforowanie i odświeżanie przez Google Play Wszystkie oceny są ponownie obliczane dla każdego żądania
Limity
Żądania na aplikację dziennie Domyślnie 10 000 (można poprosić o zwiększenie) Domyślnie 10 000 (można poprosić o zwiększenie)
Żądania na instancję aplikacji na minutę Rozgrzewka: 5 na minutę
Tokeny integralności: bez limitu publicznego*
Tokeny integralności: 5 na minutę
Ochrona
Zapobieganie manipulacjom i podobnym atakom Użyj pola requestHash Użyj pola nonce z powiązaniem treści na podstawie danych żądania
Minimalizuj ryzyko ponownych ataków i podobnych ataków Automatyczne ograniczanie ryzyka przez Google Play Użyj pola nonce z logiką po stronie serwera

* Wszystkie żądania, w tym te bez ograniczeń publicznych, podlegają niepublicznym limitom obrony o wysokich wartościach;

Rzadko rób żądania klasyczne

Wygenerowanie tokena integralności wymaga czasu, danych i baterii, a każda aplikacja ma maksymalnej liczby żądań klasycznych, które może wysłać dziennie. Dlatego wysyłaj klasyczne żądania tylko w celu sprawdzenia najwyższej wartości lub najbardziej wrażliwych działań są prawdziwe, gdy potrzebujesz dodatkowej gwarancji w stosunku do żądania standardowego. Ty nie powinny wysyłać klasycznych żądań działań o wysokiej częstotliwości lub niskiej wartości. Przeciwwskazania Wykonuj klasyczne żądania za każdym razem, gdy aplikacja przechodzi na pierwszy plan minut w tle i unikaj dzwonienia z dużej liczby urządzeń o tej samej nazwie. Jeśli aplikacja wykonuje zbyt wiele wywołań klasycznych żądań, może być ograniczona ochrony użytkowników przed nieprawidłowymi implementacjami.

Unikaj wyników buforowania

Zapisanie wyniku w pamięci podręcznej zwiększa ryzyko ataków, takich jak wydobycie i ponowne odtworzenie, w których przypadku pozyskano dobrą ocenę z niezaufanego środowiska. Jeśli rozważenie utworzenia klasycznego żądania, a następnie buforowania go w celu późniejszego użycia. jest zalecany w celu wykonania standardowego żądania na żądanie. Żądania standardowe biorą udział w pamięci podręcznej na urządzeniu, ale Google Play korzysta z dodatkowej ochrony technikom ograniczającym ryzyko ponownego ataków i wydobycia danych.

Używaj pola liczby jednorazowej do ochrony żądań klasycznych

Interfejs Play Integrity API zawiera pole o nazwie nonce, które może służyć do: dodatkowo chronić aplikację przed niektórymi atakami, takimi jak ponowne odtwarzanie czy modyfikowanie; ataków. Interfejs Play Integrity API zwraca wartość ustawioną w tym polu (w środku) podpisaną odpowiedź dotyczącą integralności. Postępuj zgodnie ze wskazówkami dotyczącymi generowania zabezpieczeńaby chronić aplikację przed atakami.

Ponawiaj próby żądań klasycznych ze wzrastającym czasem do ponowienia

warunki środowiskowe, takie jak niestabilne połączenie z internetem czy może spowodować niepowodzenie testów integralności. Może to prowadzić do: dla urządzenia, które jest wiarygodne pod innym względem, nie są generowane żadne etykiety. Do aby ograniczyć takie przypadki, uwzględnij opcję ponawiania ze wzrastającym czasem do ponowienia.

Omówienie

Diagram sekwencji przedstawiający ogólny projekt Play Integrity.
Interfejs API

Gdy użytkownik wykona w Twojej aplikacji działanie o dużej wartości, które chcesz chronić podczas kontroli integralności, wykonaj te czynności:

  1. Backend aplikacji po stronie serwera generuje i wysyła unikalną wartość do funkcji po stronie klienta. W pozostałych krokach ta logika jest nazywana „aplikacją”.
  2. Twoja aplikacja tworzy nonce z unikalnej wartości i zawartości działanie o dużej wartości. Następnie wywołuje interfejs Play Integrity API, przekazując kod nonce
  3. Twoja aplikacja otrzymuje podpisaną i zaszyfrowaną ocenę od Play Integrity API.
  4. Aplikacja przekazuje podpisaną i zaszyfrowaną ocenę do backendu aplikacji.
  5. Backend aplikacji wysyła ocenę do serwera Google Play. Google Serwer Play odszyfrowuje i weryfikuje ocenę, zwracając wyniki na serwer z backendem aplikacji.
  6. Backend aplikacji decyduje, co zrobić dalej, na podstawie sygnałów ładunek tokena.
  7. Backend aplikacji wysyła do niej wyniki decyzji.

Wygeneruj liczbę jednorazową

Gdy chronisz działanie w aplikacji za pomocą interfejsu Play Integrity API, możesz: wykorzystują pole nonce do łagodzenia określonych typów ataków, takich jak: zmanipulowania i powtórnego odtworzenia pliku cookie (ang. person in the middle, PITM). Gra Integrity API zwraca wartość ustawioną w tym polu w komunikacie podpisanym odpowiedź dotyczącą integralności.

Wartość ustawiona w polu nonce musi być prawidłowo sformatowana:

  • String
  • Odpowiednie w adresie URL
  • Zakodowane w formacie Base64 bez zawijania wierszy
  • Minimum 16 znaków
  • Maksymalnie 500 znaków.

Oto kilka typowych sposobów korzystania z pola nonce w Google Play Integrity API. Aby uzyskać najsilniejszą ochronę zapewnianą przez nonce, możesz łączyć poniższe metody.

Dołącz hasz żądania, aby zabezpieczyć się przed modyfikacją

Parametru nonce możesz używać w klasycznym żądaniu do interfejsu API podobnie jak requestHash w standardowym żądaniu interfejsu API do ochrony treści przed manipulowaniem.

Gdy poprosisz o ocenę integralności:

  1. Oblicza podsumowanie wszystkich krytycznych parametrów żądań (np. SHA256 stabilnej wersji żądania serializacji) z działania użytkownika lub żądania serwera, co się dzieje.
  2. Użyj setNonce, aby w polu nonce ustawić wartość obliczonego skrótu.

Gdy otrzymasz ocenę integralności:

  1. Zdekoduj i zweryfikuj token integralności oraz uzyskaj skrót z nonce.
  2. Oblicz podsumowanie żądania w taki sam sposób jak w aplikacji (np. SHA256 stabilnego żądania serializacji).
  3. Porównaj skróty po stronie aplikacji i serwera. Jeśli nie są zgodne, żądanie nie jest wiarygodne.
.

Uwzględniaj unikalne wartości, aby chronić się przed atakami typu replay

Aby szkodliwi użytkownicy nie wykorzystywali poprzednich odpowiedzi interfejsu Play Integrity API, możesz użyć pola nonce, aby jednoznacznie określić każdy z nich. .

Gdy poprosisz o ocenę integralności:

  1. Uzyskanie unikalnej globalnie wartości w sposób, którego szkodliwi użytkownicy nie mogą przewidzieć. Na przykład zabezpieczona kryptograficznie liczba losowa wygenerowana na po stronie serwera może być taką wartością lub istniejącym wcześniej identyfikatorem, takim jak sesja lub identyfikator transakcji. Prostszym i mniej bezpiecznym wariantem jest generowanie losowego z numeru telefonu. Zalecamy tworzenie wartości 128-bitowych lub większych.
  2. Wywołaj setNonce(), aby w polu nonce ustawić unikalną wartość uzyskaną w kroku 1.

Gdy otrzymasz ocenę integralności:

  1. Zdekoduj i zweryfikuj token integralności oraz uzyskaj unikalną wartość z parametru nonce.
  2. Jeśli wartość z kroku 1 została wygenerowana na serwerze, sprawdź, czy wartość otrzymaną unikalną wartość była jedną z wygenerowanych wartości i została używanych po raz pierwszy (serwer musi zapisać rejestr wygenerowanych dla odpowiedniego czasu trwania). Jeśli została użyta otrzymana unikalna wartość już lub nie występuje w rejestrze, odrzuć prośbę
  3. Jeżeli natomiast unikalna wartość została wygenerowana na urządzeniu, sprawdź, czy wartość odebrana jest używana po raz pierwszy (serwer musi utrzymywać rekord z wartościami, które zostały już zarejestrowane przez odpowiedni okres). Jeśli otrzymany Niepowtarzalna wartość została już użyta, odrzuć żądanie.

Połącz oba zabezpieczenia przed manipulacjami i atakami typu replay (zalecane)

Pola nonce można używać do ochrony przed manipulacjami powtarzanie ataków w tym samym czasie. W tym celu wygeneruj unikalną wartość jako i uwzględnij je w swoim żądaniu. Następnie oblicz żądania hash, pamiętając, aby umieścić w haszze tę unikalną wartość. An , który łączy oba podejścia, wygląda tak:

Gdy poprosisz o ocenę integralności:

  1. Użytkownik inicjuje działanie o dużej wartości.
  2. Dla tego działania należy uzyskać unikalną wartość, zgodnie z opisem w sekcji Uwzględnij unikalne zabezpieczeń przed atakami typu replay.
  3. Przygotuj wiadomość, którą chcesz chronić. Podaj unikalną wartość z kroku 2 w wiadomości.
  4. Aplikacja oblicza podsumowanie wiadomości, którą chce chronić, opisane w sekcji Uwzględnij hasz żądania w celu zabezpieczenia przed i manipulowaniu nimi. Ponieważ wiadomość zawiera unikalną wartość unikalna wartość jest częścią skrótu.
  5. Użyj polecenia setNonce(), aby w polu nonce ustawić obliczony skrót z poprzedniego kroku.

Gdy otrzymasz ocenę integralności:

  1. Uzyskanie unikalnej wartości z żądania
  2. Zdekoduj i zweryfikuj token integralności oraz uzyskaj skrót z nonce.
  3. Zgodnie z opisem w sekcji Uwzględnianie skrótu żądania w celu ochrony przed manipulacjami ponownie obliczyć skrót po stronie serwera i sprawdzić, czy się zgadza skrót uzyskany z tokena integralności.
  4. Jak opisano w sekcji Uwzględnianie unikalnych wartości w celu ochrony przed ponownym odtworzeniem ataków, sprawdź poprawność tej unikalnej wartości.

Poniższy diagram przedstawia te kroki z wykorzystaniem danych po stronie serwera nonce:

Schemat sekwencji pokazujący, jak chronić się przed manipulacją i ponownym odtwarzaniem
ataki

Poproś o ocenę integralności

Po wygenerowaniu pliku nonce możesz poprosić Google o ocenę integralności Graj. W tym celu wykonaj następujące czynności:

  1. Utwórz IntegrityManager w sposób opisany w przykładach poniżej.
  2. Zbuduj IntegrityTokenRequest, dostarczając nonce przez setNonce() w powiązanym kreatorze. Aplikacje rozpowszechniane wyłącznie poza Google Play, a pakiety SDK również muszą określić swoje usługi Google Cloud numeru projektu za pomocą metody setCloudProjectNumber(). Aplikacje w Google Usługi Google Play są połączone z projektem Cloud w Konsoli Play i nie trzeba ustawić w żądaniu numer projektu Cloud.
  3. Użyj menedżera, aby wywołać funkcję requestIntegrityToken() i podać IntegrityTokenRequest

Kotlin

// Receive the nonce from the secure server.
val nonce: String = ...

// Create an instance of a manager.
val integrityManager =
    IntegrityManagerFactory.create(applicationContext)

// Request the integrity token by providing a nonce.
val integrityTokenResponse: Task<IntegrityTokenResponse> =
    integrityManager.requestIntegrityToken(
        IntegrityTokenRequest.builder()
             .setNonce(nonce)
             .build())

Java

import com.google.android.gms.tasks.Task; ...

// Receive the nonce from the secure server.
String nonce = ...

// Create an instance of a manager.
IntegrityManager integrityManager =
    IntegrityManagerFactory.create(getApplicationContext());

// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> integrityTokenResponse =
    integrityManager
        .requestIntegrityToken(
            IntegrityTokenRequest.builder().setNonce(nonce).build());

Jedność

IEnumerator RequestIntegrityTokenCoroutine() {
    // Receive the nonce from the secure server.
    var nonce = ...

    // Create an instance of a manager.
    var integrityManager = new IntegrityManager();

    // Request the integrity token by providing a nonce.
    var tokenRequest = new IntegrityTokenRequest(nonce);
    var requestIntegrityTokenOperation =
        integrityManager.RequestIntegrityToken(tokenRequest);

    // Wait for PlayAsyncOperation to complete.
    yield return requestIntegrityTokenOperation;

    // Check the resulting error code.
    if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
    {
        AppendStatusLog("IntegrityAsyncOperation failed with error: " +
                requestIntegrityTokenOperation.Error);
        yield break;
    }

    // Get the response.
    var tokenResponse = requestIntegrityTokenOperation.GetResult();
}

Rodzimy użytkownik

/// Create an IntegrityTokenRequest opaque object.
const char* nonce = RequestNonceFromServer();
IntegrityTokenRequest* request;
IntegrityTokenRequest_create(&request);
IntegrityTokenRequest_setNonce(request, nonce);

/// Prepare an IntegrityTokenResponse opaque type pointer and call
/// IntegerityManager_requestIntegrityToken().
IntegrityTokenResponse* response;
IntegrityErrorCode error_code =
        IntegrityManager_requestIntegrityToken(request, &response);

/// ...
/// Proceed to polling iff error_code == INTEGRITY_NO_ERROR
if (error_code != INTEGRITY_NO_ERROR)
{
    /// Remember to call the *_destroy() functions.
    return;
}
/// ...
/// Use polling to wait for the async operation to complete.
/// Note, the polling shouldn't block the thread where the IntegrityManager
/// is running.

IntegrityResponseStatus response_status;

/// Check for error codes.
IntegrityErrorCode error_code =
        IntegrityTokenResponse_getStatus(response, &response_status);
if (error_code == INTEGRITY_NO_ERROR
    && response_status == INTEGRITY_RESPONSE_COMPLETED)
{
    const char* integrity_token = IntegrityTokenResponse_getToken(response);
    SendTokenToServer(integrity_token);
}
/// ...
/// Remember to free up resources.
IntegrityTokenRequest_destroy(request);
IntegrityTokenResponse_destroy(response);
IntegrityManager_destroy();

Odszyfruj i zweryfikuj ocenę integralności

Gdy poprosisz o ocenę integralności, interfejs Play Integrity API wygeneruje token odpowiedzi. nonce, których dotyczy prośba, stanie się częścią token odpowiedzi.

Format tokena

Jest to zagnieżdżony token internetowy JSON (JWT), to JSON Web Encryption (JWE) Podpisu internetowego JSON (JWS). Komponenty JWE i JWS są reprezentowane przez kompaktowy kod serializacja ,

Algorytmy szyfrowania / podpisywania są dobrze obsługiwane w różnych tokenach JWT implementacji:

  • JWE używa A256KW do alg i A256GCM dla enc

  • JWS używa kodu ES256.

Odszyfrowywanie i weryfikowanie danych na serwerach Google (zalecane)

Interfejs Play Integrity API pozwala odszyfrować i zweryfikować ocenę integralności na na serwerach Google, które zwiększają bezpieczeństwo aplikacji. Aby to zrobić, wykonaj te czynności: kroki:

  1. Tworzenie konta usługi w projekcie Google Cloud połączonym z Twoją aplikacją.
  2. Z serwera aplikacji pobierz token dostępu ze swojego konta usługi dane logowania za pomocą zakresu playintegrity i wyślij to żądanie:

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

Odszyfruj i zweryfikuj lokalnie

Jeśli zdecydujesz się zarządzać kluczami szyfrowania odpowiedzi i je pobierać, możesz: odszyfrować i zweryfikować zwrócony token w swoim bezpiecznym środowisku serwera. Zwrócony token możesz uzyskać, używając interfejsu IntegrityTokenResponse#token() .

Poniższy przykład pokazuje, jak zdekodować klucz AES i publiczny klucz publiczny zakodowany w formacie DER Klucz EC do weryfikacji podpisu z Konsoli Play dla konkretnego języka (w naszym przypadku jest to język programowania Java) w backendzie aplikacji. Notatka że klucze są zakodowane w base64 z użyciem flag domyślnych.

Kotlin

// base64OfEncodedDecryptionKey is provided through Play Console.
var decryptionKeyBytes: ByteArray =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT)

// Deserialized encryption (symmetric) key.
var decryptionKey: SecretKey = SecretKeySpec(
    decryptionKeyBytes,
    /* offset= */ 0,
    AES_KEY_SIZE_BYTES,
    AES_KEY_TYPE
)

// base64OfEncodedVerificationKey is provided through Play Console.
var encodedVerificationKey: ByteArray =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT)

// Deserialized verification (public) key.
var verificationKey: PublicKey = KeyFactory.getInstance(EC_KEY_TYPE)
    .generatePublic(X509EncodedKeySpec(encodedVerificationKey))

Java

// base64OfEncodedDecryptionKey is provided through Play Console.
byte[] decryptionKeyBytes =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT);

// Deserialized encryption (symmetric) key.
SecretKey decryptionKey =
    new SecretKeySpec(
        decryptionKeyBytes,
        /* offset= */ 0,
        AES_KEY_SIZE_BYTES,
        AES_KEY_TYPE);

// base64OfEncodedVerificationKey is provided through Play Console.
byte[] encodedVerificationKey =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT);
// Deserialized verification (public) key.
PublicKey verificationKey =
    KeyFactory.getInstance(EC_KEY_TYPE)
        .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));

Następnie użyj tych kluczy do odszyfrowania tokena integralności (część JWE), a potem sprawdzić i wyodrębnić zagnieżdżoną część JWS.

Kotlin

val jwe: JsonWebEncryption =
    JsonWebStructure.fromCompactSerialization(integrityToken) as JsonWebEncryption
jwe.setKey(decryptionKey)

// This also decrypts the JWE token.
val compactJws: String = jwe.getPayload()

val jws: JsonWebSignature =
    JsonWebStructure.fromCompactSerialization(compactJws) as JsonWebSignature
jws.setKey(verificationKey)

// This also verifies the signature.
val payload: String = jws.getPayload()

Java

JsonWebEncryption jwe =
    (JsonWebEncryption)JsonWebStructure
        .fromCompactSerialization(integrityToken);
jwe.setKey(decryptionKey);

// This also decrypts the JWE token.
String compactJws = jwe.getPayload();

JsonWebSignature jws =
    (JsonWebSignature) JsonWebStructure.fromCompactSerialization(compactJws);
jws.setKey(verificationKey);

// This also verifies the signature.
String payload = jws.getPayload();

Powstały w ten sposób ładunek to zwykły token tekstowy zawierający integralność .