Standard-API-Anfrage stellen

Auf dieser Seite wird beschrieben, wie du Standard-API-Anfragen für Integritätsergebnisse stellst, die ab Android 5.0 (API-Level 21) unterstützt werden. Sie können jedes Mal eine API-Standardanfrage 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:

  • Bereiten Sie den Anbieter des Integritätstokens vor (einmalig): Sie müssen die Integrity API aufrufen, um den Anbieter des Integritätstokens vorzubereiten, bevor Sie das Integritätsergebnis erhalten. Das ist beispielsweise beim Starten Ihrer App oder im Hintergrund möglich, bevor das Integritätsergebnis erforderlich ist.
  • Integritätstoken anfordern (bei Bedarf): Wenn Ihre Anwendung eine Serveranfrage stellt, deren Echtheit Sie prüfen möchten, fordern Sie ein Integritätstoken an und senden es zur Entschlüsselung und Überprüfung an den Back-End-Server der Anwendung. Dann kann Ihr Backend-Server entscheiden, wie er sich verhalten soll.

Bereiten Sie den Anbieter des Integritätstokens vor (einmalig):

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

Integritätstoken anfordern (bei Bedarf):

  1. Für die Nutzeraktion, die geschützt werden muss, berechnet Ihre Anwendung den Hash der zu stellenden Anfrage mit einem geeigneten Hash-Algorithmus wie SHA256.
  2. Ihre App fordert ein Integritätstoken an und übergibt den Anfrage-Hash.
  3. Deine App empfängt das signierte und verschlüsselte Integritätstoken von der Play Integrity API.
  4. Ihre App übergibt das Integritätstoken an das Back-End Ihrer App.
  5. Das Back-End Ihrer App sendet das Token an einen Google Play-Server. Der Google Play-Server entschlüsselt und überprüft das Ergebnis und gibt die Ergebnisse an das Back-End Ihrer Anwendung zurück.
  6. Das Back-End Ihrer Anwendung entscheidet anhand der in der Token-Nutzlast enthaltenen Signale, wie fortzufahren.
  7. Das Back-End Ihrer App sendet die Entscheidungsergebnisse an Ihre App.

Anbieter von Integritätstokens vorbereiten (einmalig)

Bevor Sie eine Standardanfrage für ein Integritätsergebnis von Google Play stellen, müssen Sie den Anbieter des Integritätstokens vorbereiten (bzw. „aufwärmen“). So kann Google Play Teilattestierungsinformationen auf dem Gerät intelligent im Cache speichern, um die Latenz im kritischen Pfad zu verringern, wenn du eine Anfrage für ein Integritätsergebnis stellst. Wenn Sie den Tokenanbieter noch einmal vorbereiten, können Sie weniger ressourcenintensive Integritätsprüfungen wiederholen, wodurch das nächste von Ihnen angeforderte Integritätsergebnis aktueller ist.

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

  • Wann Ihre App gestartet wird (z.B. beim Kaltstart). Die Vorbereitung des Tokenanbieters erfolgt asynchron und hat daher keine Auswirkungen auf die Startzeit. Diese Option eignet sich gut, wenn Sie kurz nach dem Start der App eine Anfrage zum Integritätsergebnis stellen möchten, z. B. wenn sich ein Nutzer anmeldet oder ein Spieler einem Spiel beitritt.
  • Wenn Ihre App geöffnet wird (d. h. beim Warmstart) Beachten Sie jedoch, dass jede Anwendungsinstanz das Integritätstoken nur bis zu 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 des 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. Rufe über den Manager prepareIntegrityToken() auf und gib die 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();
}

Ich bin Muttersprachler

/// 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 Manipulationsangriffe abzuwehren. Beispiel: Ein Spiel möchte die Punktzahl des Spielers an den Back-End-Server des Spiels melden, und Ihr Server möchte sicherstellen, dass diese Punktzahl nicht durch einen Proxyserver manipuliert wurde. Die Play Integrity API gibt den Wert zurück, den du in der signierten Integritätsantwort im Feld requestHash festgelegt hast. Ohne requestHash ist das Integritätstoken nur an das Gerät gebunden, aber nicht an die spezifische Anfrage. Dadurch besteht die Möglichkeit eines Angriffs. In der folgenden Anleitung wird beschrieben, wie Sie das Feld requestHash effektiv nutzen können:

Wenn Sie ein Integritätsergebnis anfordern, gilt Folgendes:

  • Berechnen Sie einen Digest aller relevanten Anfrageparameter (z.B. SHA256 einer stabilen Anfrageserialisierung) aus der laufenden Nutzeraktion oder Serveranfrage. Der im Feld requestHash festgelegte Wert hat eine maximale Länge von 500 Byte. Nehmen Sie alle Anfragedaten von Anwendungen in requestHash auf, die für die zu überprüfende oder schützende Aktion entscheidend oder relevant sind. Das Feld requestHash ist wortwörtlich im Integritätstoken enthalten, sodass die Anfragegröße durch lange Werte erhöht werden kann.
  • Übermittle den Digest im Feld requestHash an die Play Integrity API und rufe das Integritätstoken ab.

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

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

Integritätsergebnis anfordern (bei Bedarf)

Nachdem Sie den Anbieter des Integritätstokens vorbereitet haben, können Sie Integritätsergebnisse von Google Play anfordern. Führen Sie dazu die folgenden Schritte aus:

  1. Besorgen Sie sich ein StandardIntegrityTokenProvider wie oben gezeigt.
  2. Erstellen Sie ein StandardIntegrityTokenRequest und geben Sie den Anfrage-Hash der Nutzeraktion an, die Sie über die Methode setRequestHash schützen möchten.
  3. Verwenden Sie den Anbieter des Integritätstokens, um request() aufzurufen und geben Sie dabei den 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();
}

Ich bin Muttersprachler

/// 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 Geräteintegritätsergebnisse 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 in dem Google Cloud-Projekt, das mit Ihrer Anwendung verknüpft ist.
  2. Rufen Sie auf dem Server Ihrer Anwendung das Zugriffstoken mit dem Playintegrity-Bereich aus den Anmeldedaten Ihres Dienstkontos 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

Um Replay-Angriffe abzuwehren, sorgt Google Play automatisch dafür, dass jedes Integritätstoken nicht mehrfach wiederverwendet werden kann. Wenn versucht wird, dasselbe Token wiederholt zu entschlüsseln, führt dies zu leeren Ergebnissen.