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
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:
- 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ą”.
- 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 kodnonce
- Twoja aplikacja otrzymuje podpisaną i zaszyfrowaną ocenę od Play Integrity API.
- Aplikacja przekazuje podpisaną i zaszyfrowaną ocenę do backendu aplikacji.
- Backend aplikacji wysyła ocenę do serwera Google Play. Google Serwer Play odszyfrowuje i weryfikuje ocenę, zwracając wyniki na serwer z backendem aplikacji.
- Backend aplikacji decyduje, co zrobić dalej, na podstawie sygnałów ładunek tokena.
- 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:
- Oblicza podsumowanie wszystkich krytycznych parametrów żądań (np. SHA256 stabilnej wersji żądania serializacji) z działania użytkownika lub żądania serwera, co się dzieje.
- Użyj
setNonce
, aby w polunonce
ustawić wartość obliczonego skrótu.
Gdy otrzymasz ocenę integralności:
- Zdekoduj i zweryfikuj token integralności oraz uzyskaj skrót z
nonce
. - Oblicz podsumowanie żądania w taki sam sposób jak w aplikacji (np. SHA256 stabilnego żądania serializacji).
- 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:
- 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.
- Wywołaj
setNonce()
, aby w polunonce
ustawić unikalną wartość uzyskaną w kroku 1.
Gdy otrzymasz ocenę integralności:
- Zdekoduj i zweryfikuj token integralności oraz uzyskaj unikalną wartość z parametru
nonce
. - 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ę
- 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:
- Użytkownik inicjuje działanie o dużej wartości.
- Dla tego działania należy uzyskać unikalną wartość, zgodnie z opisem w sekcji Uwzględnij unikalne zabezpieczeń przed atakami typu replay.
- Przygotuj wiadomość, którą chcesz chronić. Podaj unikalną wartość z kroku 2 w wiadomości.
- 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.
- Użyj polecenia
setNonce()
, aby w polunonce
ustawić obliczony skrót z poprzedniego kroku.
Gdy otrzymasz ocenę integralności:
- Uzyskanie unikalnej wartości z żądania
- Zdekoduj i zweryfikuj token integralności oraz uzyskaj skrót z
nonce
. - 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.
- 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
:
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:
- Utwórz
IntegrityManager
w sposób opisany w przykładach poniżej. - Zbuduj
IntegrityTokenRequest
, dostarczającnonce
przezsetNonce()
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ą metodysetCloudProjectNumber()
. 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. 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:
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:
- Tworzenie konta usługi w projekcie Google Cloud połączonym z Twoją aplikacją.
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" }'
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ść .