Integritätsergebnisse

Auf dieser Seite wird beschrieben, wie Sie das zurückgegebene Integritätsergebnis interpretieren und damit arbeiten. Unabhängig davon, ob Sie eine Standard- oder klassische API-Anfrage stellen, wird das Integritätsergebnis im selben Format mit ähnlichem Inhalt zurückgegeben. Das Integritätsergebnis kommuniziert Informationen über die Gültigkeit von Geräten, Apps und Konten. Der Server Ihrer App kann die resultierende Nutzlast in einem entschlüsselten, bestätigten Ergebnis verwenden, um zu bestimmen, wie am besten mit einer bestimmten Aktion oder Anfrage in Ihrer App fortgefahren wird.

Format des zurückgegebenen Integritätsergebnisses

Die Nutzlast ist eine JSON-Datei im Nur-Text-Format und enthält neben den vom Entwickler bereitgestellten Informationen Integritätssignale.

Die allgemeine Nutzlaststruktur sieht so aus:

{
  requestDetails: { ... }
  appIntegrity: { ... }
  deviceIntegrity: { ... }
  accountDetails: { ... }
  environmentDetails: { ... }
}

Sie müssen zuerst prüfen, ob die Werte im Feld requestDetails mit denen der ursprünglichen Anfrage übereinstimmen, bevor Sie jedes Integritätsergebnis prüfen. In den folgenden Abschnitten werden die einzelnen Felder genauer beschrieben.

Feld mit Anfragedetails

Das Feld requestDetails enthält Informationen zur Anfrage, darunter vom Entwickler bereitgestellte Informationen in der requestHash für Standardanfragen und der nonce für klassische Anfragen.

Für standardmäßige API-Anfragen:

requestDetails: {
  // Application package name this attestation was requested for.
  // Note that this field might be spoofed in the middle of the request.
  requestPackageName: "com.package.name"
  // Request hash provided by the developer.
  requestHash: "aGVsbG8gd29scmQgdGhlcmU"
  // The timestamp in milliseconds when the integrity token
  // was prepared (computed on the server).
  timestampMillis: "1675655009345"
}

Diese Werte sollten denen der ursprünglichen Anfrage entsprechen. Prüfen Sie daher den Teil requestDetails der JSON-Nutzlast. Dazu müssen requestPackageName und requestHash mit dem übereinstimmen, was in der ursprünglichen Anfrage gesendet wurde, wie im folgenden Code-Snippet gezeigt:

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("requestDetails")
val requestPackageName = requestDetails.getString("requestPackageName")
val requestHash = requestDetails.getString("requestHash")
val timestampMillis = requestDetails.getLong("timestampMillis")
val currentTimestampMillis = ...

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request
    || !requestHash.equals(expectedRequestHash)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Java

RequestDetails requestDetails =
    decodeIntegrityTokenResponse
    .getTokenPayloadExternal()
    .getRequestDetails();
String requestPackageName = requestDetails.getRequestPackageName();
String requestHash = requestDetails.getRequestHash();
long timestampMillis = requestDetails.getTimestampMillis();
long currentTimestampMillis = ...;

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request.
    || !requestHash.equals(expectedRequestHash)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Für klassische API-Anfragen:

requestDetails: {
  // Application package name this attestation was requested for.
  // Note that this field might be spoofed in the middle of the
  // request.
  requestPackageName: "com.package.name"
  // base64-encoded URL-safe no-wrap nonce provided by the developer.
  nonce: "aGVsbG8gd29scmQgdGhlcmU"
  // The timestamp in milliseconds when the request was made
  // (computed on the server).
  timestampMillis: "1617893780"
}

Diese Werte sollten denen der ursprünglichen Anfrage entsprechen. Prüfen Sie daher den Teil requestDetails der JSON-Nutzlast. Dazu müssen requestPackageName und nonce mit dem übereinstimmen, was in der ursprünglichen Anfrage gesendet wurde, wie im folgenden Code-Snippet gezeigt:

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("requestDetails")
val requestPackageName = requestDetails.getString("requestPackageName")
val nonce = requestDetails.getString("nonce")
val timestampMillis = requestDetails.getLong("timestampMillis")
val currentTimestampMillis = ...

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request. See 'Generate a nonce'
        // section of the doc on how to store/compute the expected nonce.
    || !nonce.equals(expectedNonce)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Java

JSONObject requestDetails =
    new JSONObject(payload).getJSONObject("requestDetails");
String requestPackageName = requestDetails.getString("requestPackageName");
String nonce = requestDetails.getString("nonce");
long timestampMillis = requestDetails.getLong("timestampMillis");
long currentTimestampMillis = ...;

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request. See 'Generate a nonce'
        // section of the doc on how to store/compute the expected nonce.
    || !nonce.equals(expectedNonce)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Feld für Anwendungsintegrität

Das Feld appIntegrity enthält paketbezogene Informationen.

appIntegrity: {
  // PLAY_RECOGNIZED, UNRECOGNIZED_VERSION, or UNEVALUATED.
  appRecognitionVerdict: "PLAY_RECOGNIZED"
  // The package name of the app.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  packageName: "com.package.name"
  // The sha256 digest of app certificates (base64-encoded URL-safe).
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  certificateSha256Digest: ["6a6a1474b5cbbb2b1aa57e0bc3"]
  // The version of the app.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  versionCode: "42"
}

appRecognitionVerdict kann die folgenden Werte haben:

PLAY_RECOGNIZED
Die App und das Zertifikat stimmen mit den bei Google Play vertriebenen Versionen überein.
UNRECOGNIZED_VERSION
Das Zertifikat oder der Paketname stimmt nicht mit den Google Play-Einträgen überein.
UNEVALUATED
Die Anwendungsintegrität wurde nicht bewertet. Eine erforderliche Voraussetzung wurde nicht erfüllt, z. B. dass das Gerät nicht vertrauenswürdig genug sein muss.

Prüfen Sie, ob die Anwendungsintegrität wie im folgenden Code-Snippet erwartet wird, damit das Token von einer von Ihnen erstellten Anwendung generiert wurde:

Kotlin

val appIntegrity = JSONObject(payload).getJSONObject("appIntegrity")
val appRecognitionVerdict = appIntegrity.getString("appRecognitionVerdict")

if (appRecognitionVerdict == "PLAY_RECOGNIZED") {
    // Looks good!
}

Java

JSONObject appIntegrity =
    new JSONObject(payload).getJSONObject("appIntegrity");
String appRecognitionVerdict =
    appIntegrity.getString("appRecognitionVerdict");

if (appRecognitionVerdict.equals("PLAY_RECOGNIZED")) {
    // Looks good!
}

Sie können den App-Paketnamen, die App-Version und die App-Zertifikate auch manuell prüfen.

Feld „Geräteintegrität“

Das Feld deviceIntegrity kann den einzelnen Wert deviceRecognitionVerdict mit einem oder mehreren Labels enthalten, die angeben, wie gut ein Gerät die App-Integrität erzwingen kann. Wenn ein Gerät die Kriterien von Labels nicht erfüllt, ist das Feld deviceIntegrity leer.

deviceIntegrity: {
  // "MEETS_DEVICE_INTEGRITY" is one of several possible values.
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
}

Standardmäßig kann deviceRecognitionVerdict Folgendes enthalten:

MEETS_DEVICE_INTEGRITY
Die App wird auf einem Android-Gerät mit Google Play-Diensten ausgeführt. Das Gerät besteht die Systemintegritätsprüfungen und erfüllt die Android-Kompatibilitätsanforderungen.
Leer (leerer Wert)
Die App wird auf einem Gerät ausgeführt, das Anzeichen von Angriffen (z. B. API-Hooks) oder Systemmanipulationen (z. B. Rooting) aufweist, oder sie wird nicht auf einem physischen Gerät ausgeführt (z. B. einem Emulator, der die Google Play-Integritätsprüfungen nicht besteht).

Prüfen Sie, ob deviceRecognitionVerdict wie erwartet funktioniert, wie im folgenden Code-Snippet gezeigt, damit das Token von einem vertrauenswürdigen Gerät stammt:

Kotlin

val deviceIntegrity =
    JSONObject(payload).getJSONObject("deviceIntegrity")
val deviceRecognitionVerdict =
    if (deviceIntegrity.has("deviceRecognitionVerdict")) {
        deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString()
    } else {
        ""
    }

if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) {
    // Looks good!
}

Java

JSONObject deviceIntegrity =
    new JSONObject(payload).getJSONObject("deviceIntegrity");
String deviceRecognitionVerdict =
    deviceIntegrity.has("deviceRecognitionVerdict")
    ? deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString()
    : "";

if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) {
    // Looks good!
}

Wenn Sie Probleme mit dem Testgerät haben, das die Geräteintegrität erfüllt, prüfen Sie, ob das Werkseinstellungen installiert ist (z. B. durch Zurücksetzen des Geräts) und der Bootloader gesperrt ist. Sie können auch Play Integrity API-Tests in Ihrer Play Console erstellen.

Bedingte Gerätelabels

Wenn deine App in Google Play Spiele für PC veröffentlicht wird, kann die deviceRecognitionVerdict auch das folgende Label enthalten:

MEETS_VIRTUAL_INTEGRITY
Die App wird in einem Android-Emulator mit Google Play-Diensten ausgeführt. Der Emulator besteht die Systemintegritätsprüfungen und erfüllt die grundlegenden Android-Kompatibilitätsanforderungen.

Optionale Geräteinformationen

Wenn Sie zusätzliche Labels für das Integritätsergebnis aktivieren, kann deviceRecognitionVerdict die folgenden zusätzlichen Labels enthalten:

MEETS_BASIC_INTEGRITY
Die App wird auf einem Gerät ausgeführt, das die grundlegenden Prüfungen zur Systemintegrität bestanden hat. Das Gerät erfüllt möglicherweise nicht die Kompatibilitätsanforderungen von Android und ist möglicherweise nicht für die Ausführung von Google Play-Diensten zugelassen. Es kann beispielsweise sein, dass auf dem Gerät eine unbekannte Version von Android ausgeführt wird, ein entsperrter Bootloader installiert ist oder es nicht vom Hersteller zertifiziert wurde.
MEETS_STRONG_INTEGRITY
Die App wird auf einem Android-Gerät mit Google Play-Diensten ausgeführt und bietet eine starke Garantie für die Systemintegrität, z. B. einen hardwaregestützten Proof of Boot-Integrität. Das Gerät besteht die Systemintegritätsprüfungen und erfüllt die Android-Kompatibilitätsanforderungen.

Für ein einzelnes Gerät werden im Geräteintegritätsergebnis mehrere Gerätelabels zurückgegeben, wenn jedes der Kriterien des Labels erfüllt ist.

Geräteaktivitäten in letzter Zeit (Beta)

Sie können auch die letzten Geräteaktivitäten aktivieren, um zu erfahren, wie oft Ihre App in der letzten Stunde ein Integritätstoken auf einem bestimmten Gerät angefordert hat. Sie können die letzte Geräteaktivität verwenden, um Ihre App vor unerwarteten, hyperaktiven Geräten zu schützen, die ein Hinweis auf einen aktiven Angriff sein könnten. Sie können entscheiden, wie oft jeder aktuellen Geräteaktivitätsstufe vertraut werden soll, je nachdem, wie oft Ihre App voraussichtlich auf einem typischen Gerät pro Stunde ein Integritätstoken anfordert.

Wenn Sie recentDeviceActivity erhalten, hat das Feld deviceIntegrity zwei Werte:

deviceIntegrity: {
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
  recentDeviceActivity: {
    // "LEVEL_2" is one of several possible values.
    deviceActivityLevel: "LEVEL_2"
  }
}

Die deviceActivityLevel-Definitionen unterscheiden sich je nach Modi und können einen der folgenden Werte haben:

Aktivitätslevel des letzten Geräts Standard-API-Anfrage Klassische API-Anfrage
LEVEL_1 (niedrigste) Die App hat in der letzten Stunde maximal 10 Integritätstokens auf diesem Gerät angefordert. Die App hat in der letzten Stunde maximal 5 Integritätstokens auf diesem Gerät angefordert.
LEVEL_2 Die App hat in der letzten Stunde zwischen 11 und 25 Integritätstokens auf diesem Gerät angefordert. Die App hat in der letzten Stunde zwischen 6 und 15 Integritätstokens auf diesem Gerät angefordert.
LEVEL_3 Die App hat in der letzten Stunde zwischen 26 und 50 Integritätstokens auf diesem Gerät angefordert. Die App hat in der letzten Stunde zwischen 16 und 30 Integritätstokens auf diesem Gerät angefordert.
LEVEL_4 (höchste) Die App hat in der letzten Stunde mehr als 50 Integritätstokens auf diesem Gerät angefordert. Die App hat in der letzten Stunde mehr als 30 Integritätstokens auf diesem Gerät angefordert.
UNEVALUATED Die letzten Geräteaktivitäten wurden nicht ausgewertet.

Dafür kann es verschiedene Gründe geben:

  • Das Gerät ist nicht vertrauenswürdig genug.
  • Die Version der auf dem Gerät installierten App ist Google Play nicht bekannt.
  • Technische Probleme mit dem Gerät.

Feld für Kontodetails

Das Feld accountDetails enthält den einzelnen Wert appLicensingVerdict für den Lizenzierungsstatus der Anwendung.

accountDetails: {
  // This field can be LICENSED, UNLICENSED, or UNEVALUATED.
  appLicensingVerdict: "LICENSED"
}

appLicensingVerdict kann einen der folgenden Werte haben:

LICENSED
Der Nutzer hat eine App-Berechtigung. Mit anderen Worten: Der Nutzer hat deine App bei Google Play installiert oder gekauft.
UNLICENSED
Der Nutzer hat keine App-Berechtigung. Das ist beispielsweise der Fall, wenn der Nutzer deine App per Sideload oder nicht bei Google Play herunterlädt. Sie können Nutzern das Dialogfeld LIZENZ_LIZENZIEREN einblenden, um das Problem zu beheben.
UNEVALUATED

Lizenzierungsdetails wurden nicht ausgewertet, da eine erforderliche Voraussetzung nicht erfüllt wurde.

Dafür kann es verschiedene Gründe geben:

  • Das Gerät ist nicht vertrauenswürdig genug.
  • Die Version der auf dem Gerät installierten App ist Google Play nicht bekannt.
  • Der Nutzer ist nicht in Google Play angemeldet.

Wenn du prüfen möchtest, ob der Nutzer eine App-Berechtigung für deine App hat, prüfe, ob appLicensingVerdict wie erwartet ist, wie im folgenden Code-Snippet gezeigt:

Kotlin

val accountDetails = JSONObject(payload).getJSONObject("accountDetails")
val appLicensingVerdict = accountDetails.getString("appLicensingVerdict")

if (appLicensingVerdict == "LICENSED") {
    // Looks good!
}

Java

JSONObject accountDetails =
    new JSONObject(payload).getJSONObject("accountDetails");
String appLicensingVerdict = accountDetails.getString("appLicensingVerdict");

if (appLicensingVerdict.equals("LICENSED")) {
    // Looks good!
}

Feld für Umgebungsdetails

Wenn Sie sich in der Google Play Console für das Ergebnis von Play Protect angemeldet haben, enthält Ihre API-Antwort das Feld environmentDetails. Das Feld environmentDetails enthält den einzelnen Wert playProtectVerdict, der Informationen zu Google Play Protect auf dem Gerät liefert.

environmentDetails: {
  playProtectVerdict: "NO_ISSUES"
}

playProtectVerdict kann einen der folgenden Werte haben:

NO_ISSUES
Play Protect ist aktiviert und hat auf dem Gerät keine App-Probleme gefunden.
NO_DATA
Play Protect ist aktiviert, aber es wurde noch kein Scan durchgeführt. Das Gerät oder die Play Store App wurde möglicherweise vor Kurzem zurückgesetzt.
POSSIBLE_RISK
Play Protect ist deaktiviert.
MEDIUM_RISK
Play Protect ist aktiviert und hat potenziell schädliche Apps gefunden, die auf dem Gerät installiert sind.
HIGH_RISK
Play Protect ist aktiviert und hat gefährliche Apps gefunden, die auf dem Gerät installiert sind.
UNEVALUATED

Das Ergebnis von Play Protect wurde nicht bewertet.

Dafür kann es verschiedene Gründe geben:

  • Das Gerät ist nicht vertrauenswürdig genug.
  • Nur Spiele: Das Nutzerkonto ist nicht LICENSED.

So verwendest du das Play Protect-Ergebnis

Der Back-End-Server Ihrer App kann auf Grundlage des Ergebnisses anhand Ihrer Risikotoleranz entscheiden, wie zu verfahren ist. Hier sind einige Vorschläge und mögliche Nutzeraktionen:

NO_ISSUES
Play Protect ist aktiviert und hat keine Probleme festgestellt, daher sind keine Nutzeraktionen erforderlich.
POSSIBLE_RISK und NO_DATA
Wenn der Nutzer diese Ergebnisse erhält, muss er prüfen, ob Play Protect aktiviert ist und einen Scan durchgeführt hat. NO_DATA sollte nur in seltenen Fällen angezeigt werden.
MEDIUM_RISK und HIGH_RISK
Je nach Risikotoleranz kannst du den Nutzer bitten, Play Protect zu starten und auf die Play Protect-Warnungen zu reagieren. Wenn der Nutzer diese Anforderungen nicht erfüllen kann, können Sie ihn für die Serveraktion blockieren.