Klassische API-Anfrage stellen

Wenn Sie nur vorhaben, eine Standard-API -Anfragen, die für die meisten der Entwickler*innen können Sie direkt zu Integrität . Auf dieser Seite wird beschrieben, wie Sie API-Anfragen für Integritätsergebnisse, die unter Android 4.4 (API Level 19) oder höher.

Wissenswertes

Standardmäßige und klassische Anfragen vergleichen

Sie können Standardanfragen, klassische Anfragen oder eine Kombination aus beidem senden. abhängig von den Sicherheits- und Missbrauchsanforderungen Ihrer Anwendung. Standardanfragen sind für alle Apps und Spiele geeignet und kann verwendet werden, um zu prüfen, Serveraufruf echt ist, während gleichzeitig ein gewisser Schutz vor Wiederholbarkeit bereitgestellt wird. und Daten-Exfiltration an Google Play. Klassische Anfragen sind teurer in der sind Sie dafür verantwortlich, diese richtig zu implementieren, um Daten-Exfiltration und bestimmten Angriffsarten. Klassische Anfragen sollten seltener gesendet werden häufiger als Standardanfragen, z. B. gelegentlich zur Überprüfung wenn eine sehr wertvolle oder sensible Aktion von Echtheit ist.

In der folgenden Tabelle werden die wichtigsten Unterschiede zwischen den beiden Arten von Anfragen:

Standard-API-Anfrage Klassische API-Anfrage
Voraussetzungen
Android SDK-Mindestversion erforderlich Android 5.0 (API-Level 21) oder höher Android 4.4 (API-Level 19) oder höher
Google Play-Anforderungen Google Play Store und Google Play-Dienste Google Play Store und Google Play-Dienste
Integrationsdetails
API-Aufwärmphase erforderlich ✔️ (einige Sekunden) ❌ Vorstellung
Typische Anfragelatenz Einige hundert Millisekunden Ein paar Sekunden
Mögliche Häufigkeit der Anfragen Häufig (On-Demand-Prüfung für Aktionen oder Anfragen) Selten (einmalige Prüfung auf Aktionen mit dem höchsten Wert oder auf die meisten sensiblen Anfragen)
Zeitüberschreitungen Die Aufwärmphase beträgt in der Regel weniger als 10 Sekunden, beinhaltet jedoch einen Serveraufruf. Daher wird ein langes Zeitlimit empfohlen (z. B. 1 Minute). Ergebnisanfragen erfolgen clientseitig Die meisten Anfragen dauern weniger als 10 Sekunden, sie beinhalten jedoch einen Serveraufruf, daher wird ein langes Zeitlimit empfohlen (z. B. 1 Minute).
Integritätsergebnis-Token
Enthält Geräte-, App- und Kontodetails ✔️ ✔️
Token-Caching Geschütztes Caching auf dem Gerät durch Google Play Nicht empfohlen
Token über den Google Play-Server entschlüsseln und bestätigen ✔️ ✔️
Typische Server-zu-Server-Anfragelatenz bei der Entschlüsselung 10 Millisekunden mit Verfügbarkeit über 3 Neunen 10 Millisekunden mit Verfügbarkeit über 3 Neunen
Token lokal in einer sicheren Serverumgebung entschlüsseln und verifizieren ❌ Vorstellung ✔️
Token clientseitig entschlüsseln und prüfen ❌ Vorstellung ❌ Vorstellung
Aktualität des Integritätsergebnisses Einige automatische Caching- und Aktualisierungsvorgänge von Google Play Neuberechnung aller Ergebnisse für jede Anfrage
Zeitbeschränkungen
Anfragen pro App und Tag Standardmäßig 10.000 (eine Erhöhung kann angefordert werden) Standardmäßig 10.000 (eine Erhöhung kann angefordert werden)
Anfragen pro Anwendungsinstanz und Minute Aufwärmphase: 5 pro Minute
Integritätstokens: Kein öffentliches Limit*
Integritätstokens: 5 pro Minute
Schutz
Manipulation und ähnliche Angriffe abwehren Feld „requestHash“ verwenden Feld nonce mit Inhaltsbindung basierend auf Anfragedaten verwenden
Abwehren von Replay- und ähnlichen Angriffen Automatische Schadensminderung durch Google Play Feld nonce mit serverseitiger Logik verwenden

* Alle Anfragen, einschließlich Anfragen ohne öffentliche Einschränkungen, unterliegen bei hohen Werten nicht öffentlichen Verteidigungslimits.

Klassische Anfragen selten senden

Beim Generieren eines Integritätstokens werden Zeit, Daten und Akku verbraucht. Jede App hat eine maximale Anzahl klassischer Anfragen pro Tag Daher sollten Sie Stellen Sie nur klassische Anfragen, um die Aktionen mit dem höchsten Wert oder den meisten sensiblen Aktionen zu prüfen sind authentisch, wenn Sie eine zusätzliche Garantie zu einer Standardanfrage wünschen. Ich sollten keine klassischen Anfragen für Aktionen mit hoher oder geringer Relevanz stellen. Das sollten Sie nicht tun: klassische Anfragen jedes Mal senden, wenn die App im Vordergrund oder alle paar und vermeide es, von einer großen Anzahl von Geräten gleichzeitig. Eine App, die zu viele klassische Anfragen sendet, kann auf Nutzer vor fehlerhaften Implementierungen zu schützen.

Caching-Ergebnisse vermeiden

Das Caching eines Ergebnisses erhöht das Risiko von Angriffen wie Exfiltration und Replay, wenn ein gutes Ergebnis aus einer nicht vertrauenswürdigen Umgebung wiederverwendet wird. Wenn Sie eine klassische Anfrage zu stellen und diese zur späteren Verwendung im Cache zu speichern, wird empfohlen, bei Bedarf eine Standardanfrage auszuführen. Standardanfragen Es wird auf dem Gerät im Cache gespeichert, aber Google Play verwendet zusätzlichen Schutz um das Risiko von Replay-Angriffen und Daten-Exfiltration zu minimieren.

Mit dem Nonce-Feld klassische Anfragen schützen

Die Play Integrity API bietet ein Feld namens nonce, mit dem du Ihre App zusätzlich vor bestimmten Angriffen wie Wiederholung und Manipulation schützen . Die Play Integrity API gibt den Wert zurück, den du in diesem Feld festgelegt hast die signierte Integritätsantwort. Folgen Sie der Anleitung zum Erstellen von Nonces zum Schutz Ihrer App vor Angriffen.

Klassische Anfragen mit exponentiellem Backoff wiederholen

Umgebungsbedingungen, z. B. eine instabile Internetverbindung oder ein Gerät überlastet ist, können Geräteintegritätsprüfungen fehlschlagen. Dies kann zu Es werden keine Labels für ein Gerät generiert, das anderweitig vertrauenswürdig ist. Bis Schließen Sie diese Szenarien ab, indem Sie eine Wiederholungsoption mit exponentiellem Backoff einbinden.

Übersicht

Sequenzdiagramm, das das allgemeine Design von Play Integrity zeigt
API

Wenn der Nutzer eine wertvolle Aktion in Ihrer App ausführt, die Sie schützen möchten mit einer Integritätsprüfung führen Sie die folgenden Schritte aus:

  1. Das serverseitige Back-End Ihrer App generiert einen eindeutigen Wert und sendet diesen an den clientseitige Logik. In den verbleibenden Schritten wird diese Logik als Ihre „App“ bezeichnet.
  2. Deine App erstellt das nonce aus dem eindeutigen Wert und den Inhalten deiner wertvolle Aktion. Anschließend wird die Play Integrity API aufgerufen und der Parameter nonce
  3. Deine App erhält ein signiertes und verschlüsseltes Ergebnis von Play Integrity der API erstellen.
  4. Ihre App gibt das signierte und verschlüsselte Ergebnis an das Backend Ihrer App weiter.
  5. Das Backend Ihrer App sendet das Ergebnis an einen Google Play-Server. Die Google Der Play-Server entschlüsselt und verifiziert das Ergebnis und gibt die Ergebnisse an deine Back-End der Anwendung.
  6. Das Back-End Ihrer App entscheidet anhand der Signale in der Token-Nutzlast.
  7. Das Back-End Ihrer Anwendung sendet die Entscheidungsergebnisse an die Anwendung.

Nonce generieren

Wenn du eine Aktion in deiner App mit der Play Integrity API schützst, hast du folgende Möglichkeiten: Das Feld nonce nutzen, um bestimmte Arten von Angriffen abzuwehren, z. B. Man-in-the-Middle-Angriffe (PITM) und Replay-Angriffe. The Play Die Integrity API gibt den Wert zurück, den Sie in diesem Feld innerhalb der signierten Integritätsantwort.

Der im Feld nonce festgelegte Wert muss richtig formatiert sein:

  • String
  • URL-sicher
  • Als Base64 codiert und nicht verpackt
  • Mindestens 16 Zeichen
  • Maximal 500 Zeichen

Im Folgenden sind einige gängige Möglichkeiten aufgeführt, wie das Feld nonce in Play verwendet werden kann. Integrity API verwenden. Für den bestmöglichen Schutz von nonce können Sie mithilfe der unten stehenden Methoden.

Anfrage-Hash zum Schutz vor Manipulation hinzufügen

Sie können den Parameter nonce in einer klassischen API-Anfrage ähnlich verwenden wie den requestHash in einer Standard-API-Anfrage, um den Inhalt einer Manipulationsversuche melden.

Wenn Sie ein Integritätsergebnis anfordern, gilt Folgendes:

  1. Digest aller kritischen Anfrageparameter (z.B. SHA256 eines stabilen Anfrageparameters) berechnen Serialisierung anfordern) aus der Nutzeraktion oder Serveranfrage, die was gerade passiert.
  2. Mit setNonce kannst du das Feld nonce auf den Wert des berechneten Digests festlegen.

Wenn du ein Integritätsergebnis erhältst, gilt Folgendes:

  1. Decodieren und prüfen Sie das Integritätstoken und rufen Sie den Digest vom nonce.
  2. Einen Digest der Anfrage auf dieselbe Weise wie in der App berechnen (z.B. SHA256 einer stabilen Serialisierung von Anfragen).
  3. Vergleichen Sie die App-seitigen und serverseitigen Digests. Stimmen sie nicht überein, Anfrage ist nicht vertrauenswürdig.

Eindeutige Werte zum Schutz vor Replay-Angriffen verwenden

Um zu verhindern, dass böswillige Nutzer frühere Antworten des Play Integrity API kannst du das Feld nonce verwenden, um jedes einzelne angezeigt.

Wenn Sie ein Integritätsergebnis anfordern, gilt Folgendes:

  1. Sie erhalten einen global eindeutigen Wert auf eine Weise, die böswillige Nutzer nicht vorhersagen können. Eine kryptografisch sichere Zufallszahl, die auf dem kann ein solcher Wert oder eine bereits vorhandene ID, wie etwa eine Sitzung oder eine Transaktions-ID. Eine einfachere und weniger sichere Variante besteht darin, Nummer auf dem Gerät. Wir empfehlen, Werte mit einer Größe von mindestens 128 Bit zu erstellen.
  2. Rufen Sie setNonce() auf, um das Feld nonce auf den eindeutigen Wert aus Schritt 1 zu setzen.

Wenn du ein Integritätsergebnis erhältst, gilt Folgendes:

  1. Decodieren und verifizieren Sie das Integritätstoken und rufen Sie den eindeutigen Wert vom nonce.
  2. Wurde der Wert aus Schritt 1 auf dem Server generiert, überprüfen Sie, ob der Fehlercode einer der generierten Werte ist, und dass er beim ersten Mal verwendet werden (Ihr Server muss ein Protokoll der generierten Werte für eine geeignete Dauer) Wenn der empfangene eindeutige Wert verwendet wurde Antrag bereits oder nicht im Eintrag enthalten, lehnen Sie die Anfrage ab.
  3. Wenn der eindeutige Wert auf dem Gerät generiert wurde, prüfen Sie andernfalls, ob der Fehlercode der empfangene Wert zum ersten Mal verwendet wird (Ihr Server muss eine Datensatz der bereits gesehenen Werte für eine geeignete Dauer). Wenn die empfangene eindeutigen Wert verwendet wurde, lehnen Sie die Anfrage ab.

Beide Schutzmaßnahmen gegen Manipulations- und Replay-Angriffe kombinieren (empfohlen)

Das Feld nonce kann zum Schutz vor Manipulation und von Replay-Angriffen. Generieren Sie dazu den eindeutigen Wert als und fügen Sie sie in Ihre Anfrage ein. Berechnen Sie dann die Anforderungs-Hash und achten Sie darauf, dass der eindeutige Wert Teil des Hash ist. Eine Implementierung, die beide Ansätze kombiniert:

Wenn Sie ein Integritätsergebnis anfordern, gilt Folgendes:

  1. Der Nutzer initiiert die wertvolle Aktion.
  2. Erfassen Sie einen eindeutigen Wert für diese Aktion, wie unter Eindeutig von Werte zum Schutz vor Replay-Angriffen.
  3. Bereiten Sie eine Nachricht vor, die Sie schützen möchten. Eindeutigen Wert aus Schritt 2 hinzufügen in der Nachricht.
  4. Ihre App berechnet einen Digest der Nachricht, die geschützt werden soll, im Abschnitt Anfrage-Hash zum Schutz vor Abschnitt "Manipulation". Da die Nachricht die eindeutige -Wert enthält, ist der eindeutige Wert Teil des Hash.
  5. Mit setNonce() können Sie das Feld nonce auf den berechneten Digest aus dem vorherigen Schritt.

Wenn du ein Integritätsergebnis erhältst, gilt Folgendes:

  1. Eindeutigen Wert aus der Anfrage abrufen
  2. Decodieren und prüfen Sie das Integritätstoken und rufen Sie den Digest vom nonce.
  3. Wie im Abschnitt Anfrage-Hash zum Schutz vor Manipulation hinzufügen beschrieben, den Digest auf der Serverseite neu und prüfen Sie, Den aus dem Integritätstoken abgerufenen Digest.
  4. Wie im Abschnitt Eindeutige Werte zum Schutz vor Wiederholung einschließen beschrieben Angriffe die Gültigkeit des eindeutigen Werts prüfen.

Das folgende Sequenzdiagramm veranschaulicht diese Schritte mit einer serverseitigen nonce:

Sequenzdiagramm, das zeigt, wie sich Manipulationen und erneute Wiedergaben verhindern lassen
Angriffe

Integritätsergebnis anfordern

Nach dem Generieren eines nonce kannst du ein Integritätsergebnis von Google anfordern Spielen. Führen Sie dazu die folgenden Schritte aus:

  1. Erstellen Sie ein IntegrityManager, wie in den folgenden Beispielen gezeigt.
  2. Erstellen Sie ein IntegrityTokenRequest und geben Sie das nonce über die setNonce()-Methode im verknüpften Builder. Ausschließlich vertriebene Apps und SDKs ihre Google Cloud-Dienste Projektnummer mit der Methode setCloudProjectNumber(). Apps auf Google Google Play ist mit einem Cloud-Projekt in der Play Console verknüpft und muss nicht die Cloud-Projektnummer in der Anfrage festlegen.
  3. Rufen Sie requestIntegrityToken() über das Manager-Konto auf und geben Sie dabei Folgendes an: 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());

Unity

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

Ich bin Muttersprachler

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

Integritätsergebnis entschlüsseln und prüfen

Wenn du ein Integritätsergebnis anforderst, stellt die Play Integrity API ein signiertes Antworttokens. Die nonce, die Sie in Ihrer Anfrage angeben, wird Teil des Antworttokens.

Token format

Das Token ist ein verschachteltes JSON-Webtoken (JWT), das ist JSON Web Encryption (JWE) der JSON Web Signature (JWS). Die JWE- und JWS-Komponenten werden mithilfe von kompakten Serialisierung .

Die Verschlüsselungs-/Signaturalgorithmen werden in verschiedenen JWTs gut unterstützt. Implementierungen:

  • JWE verwendet A256KW für alg und A256GCM für enc

  • JWS verwendet ES256.

Auf den Google-Servern entschlüsseln und bestätigen (empfohlen)

Mit der Play Integrity API kannst du das Integritätsergebnis von den Google-Servern, was die Sicherheit Ihrer App erhöht. Füllen Sie dazu diese Schritte:

  1. Dienstkonto erstellen innerhalb des Google Cloud-Projekts, das mit Ihrer Anwendung verknüpft ist.
  2. Rufen Sie auf dem Server der Anwendung das Zugriffstoken von Ihrem Dienstkonto ab. Anmeldedaten mit dem Bereich playintegrity und stellen Sie die folgende Anfrage:

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
    '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. Lesen Sie die JSON-Antwort.

Lokal entschlüsseln und bestätigen

Wenn Sie Ihre Schlüssel zur Verschlüsselung von Antworten verwalten und herunterladen, haben Sie folgende Möglichkeiten: Entschlüsseln und verifizieren Sie das zurückgegebene Token innerhalb Ihrer eigenen sicheren Serverumgebung. Sie können das zurückgegebene Token mit dem IntegrityTokenResponse#token() abrufen. .

Das folgende Beispiel zeigt, wie der AES-Schlüssel und der DER-codierte öffentliche Schlüssel decodiert werden können EC-Schlüssel für die Signaturprüfung von der Play Console zu sprachspezifischen Problemen (in diesem Fall die Programmiersprache Java) im Back-End der Anwendung. Hinweis dass die Schlüssel mit Standard-Flags base64-codiert sind.

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

Verwenden Sie diese Schlüssel als Nächstes, um zuerst das Integritätstoken (JWE-Teil) zu entschlüsseln und dann den verschachtelten JWS-Teil zu verifizieren und zu extrahieren.

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

Die resultierende Nutzlast ist ein Nur-Text-Token, das Integrität .