Standard-API-Anfrage stellen

Auf dieser Seite wird beschrieben, wie Sie Standard-API-Anfragen für Integritätsergebnisse stellen, die unter Android 5.0 (API-Level 21) und höher unterstützt werden. Sie können jedes Mal eine Standard-API-Anfrage für ein Integritätsergebnis stellen, wenn Ihre App einen Serveraufruf sendet, um zu prüfen, ob die Interaktion echt ist.

Übersicht

Sequenzdiagramm, das das allgemeine Design der Play Integrity API zeigt

Eine Standardanfrage besteht aus zwei Teilen:

  • Integritätstoken-Anbieter vorbereiten (einmalig): Sie müssen die Integrity API aufrufen, um den Integritätstoken-Anbieter vorzubereiten, lange bevor Sie das Integritätsergebnis erhalten müssen. Das ist beispielsweise beim Start der App oder im Hintergrund möglich, bevor das Integritätsergebnis erforderlich ist.
  • Integritätstoken anfordern (bei Bedarf): Immer wenn Ihre App eine Serveranfrage stellt, die Sie prüfen möchten, fordern Sie ein Integritätstoken an und senden es zur Entschlüsselung und Prüfung an den Back-End-Server Ihrer App. Anschließend kann Ihr Backend-Server entscheiden, wie verfahren werden soll.

Bereiten Sie den Integritätstoken-Anbieter vor (einmalig):

  1. Ihre Anwendung ruft den Integritätstoken-Anbieter mit Ihrer Google Cloud-Projektnummer auf.
  2. Ihre Anwendung hält den Anbieter des Integritätstokens im Arbeitsspeicher für weitere Aufrufe der Attestierungsprüfung.

Integritätstoken anfordern (bei Bedarf):

  1. Für die Nutzeraktion, die geschützt werden muss, berechnet Ihre Anwendung den Hash der Anfrage mithilfe eines geeigneten Hash-Algorithmus wie SHA256.
  2. Ihre App fordert ein Integritätstoken an und übergibt den Anfrage-Hash.
  3. Deine App erhält das signierte und verschlüsselte Integritätstoken von der Play Integrity API.
  4. Ihre Anwendung übergibt das Integritätstoken an das Backend der Anwendung.
  5. Das Back-End Ihrer App sendet das Token an einen Google Play-Server. Der Google Play-Server entschlüsselt und verifiziert das Ergebnis und gibt die Ergebnisse an das Back-End Ihrer App zurück.
  6. Das Back-End Ihrer Anwendung entscheidet anhand der Signale in der Tokennutzlast, wie fortfahren soll.
  7. Das Back-End Ihrer App sendet die Entscheidungsergebnisse an Ihre App.

Integritätstoken-Anbieter vorbereiten (einmalig)

Bevor Sie eine Standardanfrage für ein Integritätsergebnis von Google Play senden, müssen Sie den Integritätstoken-Anbieter vorbereiten (oder „aufwärmen“). Dadurch kann Google Play partielle Attestierungsinformationen auf dem Gerät intelligent im Cache speichern, um die Latenz auf dem kritischen Pfad zu verringern, wenn Sie eine Anfrage für ein Integritätsergebnis stellen. Wenn Sie den Tokenanbieter noch einmal vorbereiten, können Sie weniger ressourcenintensive Integritätsprüfungen wiederholen. Dadurch wird das nächste von Ihnen angeforderte Integritätsergebnis auf dem neuesten Stand.

Sie können den Anbieter von Integritätstokens vorbereiten:

  • Wenn Ihre App gestartet wird (z.B. beim Kaltstart). Die Vorbereitung des Tokenanbieters ist asynchron und wirkt sich daher nicht auf die Startzeit aus. Diese Option eignet sich gut, wenn Sie kurz nach dem Start der App eine Anfrage für ein Integritätsergebnis senden möchten, z. B. wenn sich ein Nutzer anmeldet oder ein Spieler einem Spiel beitritt.
  • Wenn die App geöffnet wird (z.B. beim Warmstart). Beachten Sie jedoch, dass jede Anwendungsinstanz das Integritätstoken maximal fünfmal pro Minute vorbereiten kann.
  • Jederzeit im Hintergrund, wenn Sie das Token vor einer Anfrage zum Integritätsergebnis vorbereiten möchten.

So bereiten Sie den Anbieter von Integritätstokens vor:

  1. Erstellen Sie ein StandardIntegrityManager, wie in den folgenden Beispielen gezeigt.
  2. Erstellen Sie ein PrepareIntegrityTokenRequest und geben Sie die Google Cloud-Projektnummer über die Methode setCloudProjectNumber() an.
  3. Rufen Sie über das Manager prepareIntegrityToken() auf und geben Sie das PrepareIntegrityTokenRequest an.

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

Unity

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

Native Anzeige

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

Anfragen vor Manipulation schützen (empfohlen)

Wenn Sie eine Nutzeraktion in Ihrer App mit der Play Integrity API prüfen, können Sie das Feld requestHash verwenden, um Manipulationsangriffen entgegenzuwirken. Beispielsweise kann ein Spiel die Punktzahl des Spielers an den Back-End-Server des Spiels melden, und Ihr Server möchte sicherstellen, dass diese Punktzahl nicht von einem Proxyserver manipuliert wurde. Die Play Integrity API gibt den Wert zurück, den Sie im Feld requestHash in der signierten Integritätsantwort festgelegt haben. Ohne das requestHash ist das Integritätstoken nur an das Gerät gebunden, aber nicht an die spezifische Anfrage, was eine Angriffsmöglichkeit eröffnet. In der folgenden Anleitung wird beschrieben, wie Sie das Feld requestHash effektiv nutzen:

Wenn Sie ein Integritätsergebnis anfordern, geschieht Folgendes:

  • Berechnen Sie einen Digest aller relevanten Anfrageparameter (z.B. SHA256 einer stabilen Anfrageserialisierung) aus der Nutzeraktion oder Serveranfrage. Der im Feld requestHash festgelegte Wert hat eine maximale Länge von 500 Byte. Nehmen Sie alle App-Anfragedaten in die requestHash auf, die für die Aktion, die Sie prüfen oder schützen, entscheidend oder relevant sind. Das Feld requestHash ist wortgetreu im Integritätstoken enthalten, sodass durch lange Werte die Anfragegröße erhöht werden kann.
  • Stelle den Digest als Feld requestHash für die Play Integrity API bereit und rufe das Integritätstoken ab.

Wenn du ein Integritätsergebnis erhältst:

  • Decodieren Sie das Integritätstoken und extrahieren Sie das Feld requestHash.
  • Berechnet einen Digest der Anfrage auf dieselbe Weise wie in der Anwendung (z.B. SHA256 einer stabilen Anfrageserialisierung).
  • Vergleichen Sie die App- und serverseitigen Digests. Stimmen sie nicht überein, ist die Anfrage nicht vertrauenswürdig.

Integritätsergebnis anfordern (auf Anfrage)

Nachdem Sie den Integritätstoken-Anbieter vorbereitet haben, können Sie damit beginnen, Integritätsergebnisse von Google Play anzufordern. Führen Sie dazu die folgenden Schritte aus:

  1. Rufen Sie ein StandardIntegrityTokenProvider ab, wie oben gezeigt.
  2. Erstellen Sie ein StandardIntegrityTokenRequest und geben Sie den Anfrage-Hash der Nutzeraktion an, die Sie mit der Methode setRequestHash schützen möchten.
  3. Rufen Sie mit dem Integritätstoken-Anbieter request() auf und geben Sie das StandardIntegrityTokenRequest an.

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

Unity

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

Native Anzeige

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

Integritätsergebnis entschlüsseln und prüfen

Nachdem Sie ein Integritätsergebnis angefordert haben, stellt die Play Integrity API ein verschlüsseltes Antworttoken bereit. Um die Ergebnisse der Geräteintegrität zu erhalten, müssen Sie das Integritätstoken auf den Google-Servern entschlüsseln. Führen Sie dazu die folgenden Schritte aus:

  1. Erstellen Sie ein Dienstkonto innerhalb des Google Cloud-Projekts, das mit Ihrer Anwendung verknüpft ist. Bei der Kontoerstellung müssen Sie dem Dienstkonto die Rollen Service Account User und Service Usage-Nutzer zuweisen.
  2. Rufen Sie auf dem App-Server das Zugriffstoken aus den Anmeldedaten Ihres Dienstkontos mithilfe des playintegrity-Bereichs ab und stellen Sie die folgende Anfrage:

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

Die resultierende Nutzlast ist ein Nur-Text-Token, das Integritätsergebnisse enthält.

Automatischer Wiederholungsschutz

Zur Abschwächung von Replay-Angriffen stellt Google Play automatisch sicher, dass jedes Integritätstoken nicht mehrfach wiederverwendet werden kann. Der Versuch, wiederholt dasselbe Token zu entschlüsseln, führt zu leeren Ergebnissen.