קביעות התקינות

בדף הזה מוסבר איך לפרש את פסק הדין שמוחזר לגבי תקינות המערכת ואיך לעבוד איתו. לא משנה אם שולחים בקשת API רגילה או קלאסית, קביעת התקינות מוחזרת באותו פורמט עם תוכן דומה. התוצאה של בדיקת התקינות מספקת מידע על התוקף של מכשירים, אפליקציות וחשבונות. השרת של האפליקציה יכול להשתמש במטען הייעודי (payload) שמתקבל בתוצאה מפוענחת ומאומתת, כדי לקבוע מהי הדרך הטובה ביותר להמשיך בפעולה או בבקשה מסוימת באפליקציה.

פורמט קביעת התקינות שמוחזר

המטען הייעודי הוא JSON בטקסט פשוט, והוא מכיל אותות יושרה לצד מידע שהמפתח סיפק.

המבנה הכללי של המטען הייעודי (payload) הוא כזה:

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

לפני שבודקים כל פסיקה לגבי תקינות, צריך לוודא שהערכים בשדה requestDetails תואמים לערכים בבקשה המקורית. בקטעים הבאים מפורט כל שדה.

שדה פרטי הבקשה

השדה requestDetails מכיל מידע על הבקשה, כולל מידע שהמפתח סיפק בשדה requestHash לבקשות רגילות ובשדה nonce לבקשות קלאסיות.

בקשות API רגילות:

"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 requested.
  "timestampMillis": "1675655009345"
}

הערכים האלה צריכים להיות זהים לאלה של הבקשה המקורית. לכן, צריך לוודא שהחלק requestDetails במטען הייעודי (payload) של JSON תואם לערכים של requestPackageName ושל requestHash שנשלחו בבקשה המקורית, כפי שמוצג בקטע הקוד הבא:

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.
        ...
}

לגבי בקשות API קלאסיות:

"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"
}

הערכים האלה צריכים להיות זהים לאלה של הבקשה המקורית. לכן, צריך לוודא שהחלק requestDetails במטען הייעודי (payload) של JSON תואם לערכים של requestPackageName ושל nonce שנשלחו בבקשה המקורית, כפי שמוצג בקטע הקוד הבא:

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.
        ...
}

שדה מהימנות האפליקציה

השדה appIntegrity מכיל מידע שקשור לחבילה.

"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 יכול לקבל את הערכים הבאים:

PLAY_RECOGNIZED
האפליקציה והאישור תואמים לגרסאות שמופצות על ידי Google Play.
UNRECOGNIZED_VERSION
האישור או שם החבילה לא תואמים לרשומות ב-Google Play.
UNEVALUATED
לא בוצעה הערכה של מהימנות האפליקציה
. לא עמדת בדרישה נדרשת, למשל המכשיר לא נחשב מהימן מספיק.

כדי לוודא שהאסימון נוצר על ידי אפליקציה שיצרתם, צריך לוודא ששלמות האפליקציה היא כצפוי, כמו שמוצג בקטע הקוד הבא:

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!
}

אפשר גם לבדוק את שם החבילה של האפליקציה, את גרסת האפליקציה ואת האישורים של האפליקציה באופן ידני.

שדה תקינות המכשיר

השדה deviceIntegrity יכול להכיל ערך יחיד, deviceRecognitionVerdict, עם תווית אחת או יותר שמייצגת את רמת האכיפה של שלמות האפליקציה במכשיר. אם מכשיר לא עומד בקריטריונים של אף אחת מהתוויות, השדה deviceIntegrity לא יכלול את deviceRecognitionVerdict.

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

כברירת מחדל, deviceRecognitionVerdict יכול להכיל את הרכיבים הבאים:

MEETS_DEVICE_INTEGRITY
האפליקציה פועלת במכשיר Android מקורי ומאושר. ב-Android מגרסה 13 ואילך, יש הוכחה שמגובה בחומרה לכך שתוכנת האתחול של המכשיר נעולה ומערכת ההפעלה של Android שנטענה היא תמונה מאושרת של יצרן המכשיר.
ריק (ערך ריק)
האפליקציה פועלת במכשיר שיש בו סימנים למתקפה (למשל, API hooking) או לפריצה למערכת (למשל, rooting), או שהאפליקציה לא פועלת במכשיר פיזי (למשל, אמולטור שלא עובר את בדיקות התקינות של Google Play).

כדי לוודא שהטוקן הגיע ממכשיר מהימן, צריך לוודא שהערך של deviceRecognitionVerdict הוא כמצופה, כמו שמוצג בקטע הקוד הבא:

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!
}

אם נתקלתם בבעיות בבדיקת תקינות המכשיר שלכם, ודאו שמותקן בו ה-ROM המקורי (למשל, על ידי איפוס המכשיר) ושתוכנת האתחול נעולה. אפשר גם ליצור בדיקות של Play Integrity API ב-Play Console.

תוויות מותנות למכשירים

אם האפליקציה שלך מופצת ב-Google Play Games למחשב, יכול להיות שdeviceRecognitionVerdict תכלול גם את התווית הבאה:

MEETS_VIRTUAL_INTEGRITY
האפליקציה פועלת באמולטור מבוסס-Android עם Google Play Services. האמולטור עובר את בדיקות התקינות של המערכת ועומד בדרישות התאימות הבסיסיות של Android.

מידע אופציונלי על המכשיר ואחזור נתונים על המכשיר

אם תביעו הסכמה לקבלת תוויות נוספות בקביעת התקינות, deviceRecognitionVerdict עשוי להכיל את התוויות הנוספות הבאות:

MEETS_BASIC_INTEGRITY
האפליקציה פועלת במכשיר שעובר את הבדיקות הבסיסיות של תקינות המערכת. אפשר לנעול או לבטל את הנעילה של תוכנת האתחול של המכשיר, ומצב ההפעלה יכול להיות מאומת או לא מאומת. יכול להיות שהמכשיר לא מאושר, ובמקרה כזה Google לא יכולה לספק שום הבטחות לגבי אבטחה, פרטיות או תאימות לאפליקציות. ב-Android מגרסה 13 ואילך, MEETS_BASIC_INTEGRITYהתוצאה של הבדיקה דורשת רק ש-Google תספק את הבסיס המהימן של האימות.
MEETS_STRONG_INTEGRITY
האפליקציה פועלת במכשיר Android מקורי ומאושר עם עדכון אבטחה מהזמן האחרון.
  • ב-Android 13 ומעלה, כדי לקבל את התוצאה MEETS_STRONG_INTEGRITY נדרשים MEETS_DEVICE_INTEGRITY עדכוני אבטחה בשנה האחרונה לכל המחיצות במכשיר, כולל תיקון של מחיצת מערכת ההפעלה Android ותיקון של מחיצת הספק.
  • ב-Android 12 ובגרסאות קודמות, כדי לקבל את קביעת התקינות MEETS_STRONG_INTEGRITY נדרש רק אימות מבוסס חומרה של תקינות ההפעלה, ולא נדרש שהמכשיר יכלול עדכון אבטחה מהזמן האחרון. לכן, כשמשתמשים ב-MEETS_STRONG_INTEGRITY, מומלץ לקחת בחשבון גם את גרסת Android SDK בשדה deviceAttributes.

אם מכשיר אחד עומד בקריטריונים של כמה תוויות, הוא יחזיר כמה תוויות מכשיר בתוצאת בדיקת תקינות המכשיר.

מאפייני המכשיר

אפשר גם להביע הסכמה לשיתוף מאפייני המכשיר, שכוללים את גרסת Android SDK של מערכת ההפעלה Android שפועלת במכשיר. יכול להיות שבעתיד נרחיב את התכונה הזו ונוסיף לה מאפיינים נוספים של מכשירים.

הערך של גרסת ה-SDK הוא מספר גרסת Android SDK שמוגדר ב-Build.VERSION_CODES. גרסת ה-SDK לא נבדקת אם לא עמדתם בדרישה כלשהי. במקרה הזה, השדה sdkVersion לא מוגדר, ולכן השדה deviceAttributes ריק. למה זה קורה?

  • המכשיר לא מספיק מהימן.
  • היו בעיות טכניות במכשיר.

אם תבחרו להצטרף לקבלת deviceAttributes, בשדה deviceIntegrity יופיע השדה הנוסף הבא:

"deviceIntegrity": {
  "deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"],
  "deviceAttributes": {
    // 33 is one possible value, which represents Android 13 (Tiramisu).
    "sdkVersion": 33
  }
}

אם גרסת ה-SDK לא נבדקת, השדה deviceAttributes יוגדר כך:

"deviceIntegrity": {
  "deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"],
  "deviceAttributes": {}  // sdkVersion field is not set.
}

פעילות במכשיר מהזמן האחרון

אתם יכולים גם להביע הסכמה לשימוש באות 'פעילות במכשיר מהזמן האחרון', שמציין כמה פעמים האפליקציה שלכם ביקשה טוקן תקינות במכשיר ספציפי בשעה האחרונה. אתם יכולים להשתמש בפעילות האחרונה במכשיר כדי להגן על האפליקציה מפני מכשירים לא צפויים והיפראקטיביים, שיכולים להעיד על מתקפה פעילה. אתם יכולים להחליט עד כמה אתם סומכים על כל רמה של פעילות במכשיר מהזמן האחרון, על סמך מספר הפעמים שאתם מצפים שהאפליקציה שלכם תבקש טוקן תקינות בכל שעה במכשיר טיפוסי.

אם תביעו הסכמה לקבלת recentDeviceActivity, בשדה deviceIntegrity יהיו שני ערכים:

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

ההגדרות של deviceActivityLevel שונות בין המצבים, והערך יכול להיות אחד מהערכים הבאים:

רמת הפעילות במכשיר מהזמן האחרון בקשות רגילות לטוקן תקינות של API במכשיר הזה בשעה האחרונה לכל אפליקציה בקשות קלאסיות לטוקן תקינות במכשיר הזה בשעה האחרונה לכל אפליקציה
LEVEL_1 (הנמוך ביותר) 10 או פחות 5 או פחות
LEVEL_2 בין 11 ל-25 בין 6 ל-10
LEVEL_3 בין 26 ל-50 בין 11 ל-15
LEVEL_4 (הגבוה ביותר) יותר מ-50 יותר מ-15
UNEVALUATED לא בוצעה הערכה של פעילות במכשיר מהזמן האחרון. זה יכול לקרות בגלל:
  • המכשיר לא מספיק מהימן.
  • גרסת האפליקציה שמותקנת במכשיר לא מוכרת ל-Google Play.
  • בעיות טכניות במכשיר.

זכירת נתונים שמשויכים למכשיר (בטא)

אתם יכולים גם להפעיל את התכונה שחזור נתונים לפי מכשיר, שמאפשרת לכם לאחסן נתונים מסוימים בהתאמה אישית, שמשויכים למכשירים ספציפיים, ולבצע להם ריקול באופן מהימן כשהאפליקציה מותקנת מחדש במכשיר הזה במועד מאוחר יותר. אחרי שמבקשים טוקן יושרה, מבצעים הפעלה נפרדת משרת לשרת כדי לשנות את ערכי ההחזרה של המכשיר עבור מכשיר ספציפי.

אם תצטרפו ל-deviceRecall, השדה deviceIntegrity יכיל את פרטי הריקול של המכשיר שהגדרתם עבור המכשיר הספציפי:

"deviceIntegrity": {
  "deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"],
  "deviceRecall": {
    "values": {
      "bitFirst": true,
      "bitSecond": false,
      "bitThird": true
    },
    "writeDates": {
      // Write time in YYYYMM format in UTC.
      "yyyymmFirst": 202401,
      // Note that yyyymmSecond is not set because bitSecond is false.
      "yyyymmThird": 202310
    }
  }
}

השדה deviceRecall פוצל לשני שדות:

  • values: שחזור ערכי הביטים שהגדרתם בעבר למכשיר הזה.
  • writeDates: תאריכי כתיבת הביטים ב-UTC, ברמת דיוק של שנה וחודש. תאריך הכתיבה של ביט ההחזרה יתעדכן בכל פעם שהביט מוגדר לערך true, ויוסר כשהביט מוגדר לערך false.

אם מידע על החזרת המכשיר לא זמין, הערך של החזרת המכשיר יהיה ריק:

"deviceIntegrity": {
  "deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"],
  "deviceRecall": {
    "values": {},
    "writeDates": {}
  }
}

השדה של פרטי החשבון

השדה accountDetails מכיל ערך יחיד, appLicensingVerdict, שמייצג את סטטוס הרישיון של האפליקציה ב-Google Play עבור חשבון המשתמש שמחובר למכשיר. אם לחשבון המשתמש יש רישיון Play לאפליקציה, זה אומר שהמשתמש הוריד או קנה אותה מ-Google Play.

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

appLicensingVerdict יכול לקבל את אחד מהערכים הבאים:

LICENSED
למשתמש יש הרשאה לאפליקציה. במילים אחרות, המשתמש התקין או עדכן את האפליקציה שלך מ-Google Play במכשיר שלו.
UNLICENSED
למשתמש אין הרשאה להשתמש באפליקציה. זה קורה למשל כשהמשתמש מתקין את האפליקציה בשיטה חלופית, או לא מתקין אותה דרך Google Play. כדי לפתור את הבעיה, אפשר להציג למשתמשים את תיבת הדו-שיח GET_LICENSED.
UNEVALUATED

לא בוצעה הערכה של פרטי הרישיון כי לא עמדת בדרישה נדרשת.

יכולות להיות לכך כמה סיבות, כולל:

  • המכשיר לא מספיק מהימן.
  • גרסת האפליקציה שמותקנת במכשיר לא ידועה ל-Google Play.
  • המשתמש לא מחובר ל-Google Play.

כדי לוודא שלמשתמש יש הרשאה להשתמש באפליקציה, צריך לבדוק שהערך של appLicensingVerdict הוא כמו שציפיתם, כמו שמוצג בקטע הקוד הבא:

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!
}

שדה פרטי הסביבה

אפשר גם להפעיל אותות נוספים לגבי הסביבה. האות 'סיכון לגישה לאפליקציה' מאפשר לאפליקציה לדעת אם יש אפליקציות פועלות אחרות שיכולות לצלם את המסך, להציג שכבות-על או לשלוט במכשיר. הסטטוס של Play Protect מציין אם Google Play Protect מופעל במכשיר ואם הוא זיהה תוכנות זדוניות מוכרות.

אם בחרתם להשתמש בתוצאת הסיכון לגישה לאפליקציה או בתוצאת Play Protect ב-Google Play Console, התגובה של ה-API תכלול את השדה environmentDetails. השדה environmentDetails יכול להכיל שני ערכים: appAccessRiskVerdict ו-playProtectVerdict.

קביעת סיכון הגישה לאפליקציה

אחרי ההפעלה, השדה environmentDetails במטען הייעודי (payload) של Play Integrity API יכיל את קביעת הסיכון החדשה לגישה לאפליקציה.

{
  "requestDetails": { ... },
  "appIntegrity": { ... },
  "deviceIntegrity": { ... },
  "accountDetails": { ... },
  "environmentDetails": {
      "appAccessRiskVerdict": {
          // This field contains one or more responses, for example the following.
          "appsDetected": ["KNOWN_INSTALLED", "UNKNOWN_INSTALLED", "UNKNOWN_CAPTURING"]
      }
 }
}

אם בוצעה הערכה של סיכון הגישה לאפליקציה, appAccessRiskVerdict מכיל את השדה appsDetected עם תשובה אחת או יותר. התשובות האלה מחולקות לשתי קבוצות, בהתאם למקור ההתקנה של האפליקציות שזוהו:

  • אפליקציות של Play או מערכת: אפליקציות שמותקנות על ידי Google Play או שנטענות מראש על ידי יצרן המכשיר במחיצת המערכת של המכשיר (מזוהות באמצעות FLAG_SYSTEM). התשובות לאפליקציות כאלה מתחילות ב-KNOWN_.

  • אפליקציות אחרות: אפליקציות שלא מותקנות דרך Google Play. האפליקציות שמוחרגות הן אלה שנטענו מראש במחיצת המערכת על ידי יצרן המכשיר. התשובות לאפליקציות כאלה מתחילות בקידומת UNKNOWN_.

אלה התשובות שיכולות להתקבל:

KNOWN_INSTALLED, UNKNOWN_INSTALLED
יש אפליקציות מותקנות שתואמות למקור ההתקנה המתאים.
KNOWN_CAPTURING, UNKNOWN_CAPTURING
יש אפליקציות שפועלות עם הרשאות מופעלות שיכולות לשמש לצפייה במסך בזמן שהאפליקציה שלך פועלת. ההגדרה הזו לא כוללת שירותי נגישות מאומתים שפועלים במכשיר ומזוהים על ידי Google Play.
KNOWN_CONTROLLING, UNKNOWN_CONTROLLING
יש אפליקציות שפועלות במכשיר עם הרשאות מופעלות, שאפשר להשתמש בהן כדי לשלוט במכשיר ולשלוט ישירות בקלט של האפליקציה, ואפשר להשתמש בהן כדי לתעד את הקלט והפלט של האפליקציה. ההחרגה הזו לא כוללת שירותי נגישות מאומתים שפועלים במכשיר ומוכרים ל-Google Play.
KNOWN_OVERLAYS, UNKNOWN_OVERLAYS
יש אפליקציות שפועלות עם הרשאות מופעלות שיכולות לשמש להצגת שכבות-על באפליקציה. לא כולל שירותי נגישות מאומתים שמוכרים ל-Google Play ופועלים במכשיר.
ריק (ערך ריק)

אם לא עמדתם בדרישה כלשהי, לא נבדוק את הסיכון לגישה לאפליקציה. במקרה הזה, השדה appAccessRiskVerdict ריק. יכולות להיות לכך כמה סיבות, כולל:

  • המכשיר לא מספיק מהימן.
  • גורם הצורה של המכשיר הוא לא טלפון, טאבלט או מכשיר מתקפל.
  • במכשיר לא מותקנת מערכת Android מגרסה 6 (רמת API‏ 23) ואילך.
  • גרסת האפליקציה שמותקנת במכשיר לא מוכרת ל-Google Play.
  • הגרסה של חנות Google Play במכשיר לא עדכנית.
  • לחשבון המשתמש אין רישיון ל-Play.
  • נעשה שימוש בבקשה רגילה עם הפרמטר verdictOptOut.
  • נעשה שימוש בבקשה רגילה עם גרסה של ספריית Play Integrity API שעדיין לא תומכת בסיכון לגישה לאפליקציה בבקשות רגילות.

הסיכון לגישה לאפליקציה מחריג באופן אוטומטי שירותי נגישות מאומתים שעברו בדיקת נגישות משופרת ב-Google Play (שמותקנים על ידי חנות אפליקציות כלשהי במכשיר). המשמעות של 'לא נכלל' היא ששירותי נגישות מאומתים שפועלים במכשיר לא יחזירו תשובה לגבי צילום מסך, שליטה או שכבות-על בתוצאת הסיכון לגישה לאפליקציה. כדי לבקש בדיקת נגישות משופרת של Google Play לאפליקציית הנגישות שלכם, אתם צריכים לפרסם אותה ב-Google Play ולוודא שהערך של הדגל isAccessibilityTool מוגדר כ-true במניפסט של האפליקציה, או לבקש בדיקה.

דוגמאות לקביעות סיכון הגישה לאפליקציה

בטבלה הבאה מוצגות כמה דוגמאות לתוצאות של סיכון גישה לאפליקציה והמשמעות שלהן (הטבלה לא כוללת את כל התוצאות האפשריות):

דוגמה לתגובה של קביעת סיכון הגישה לאפליקציה פירוש
appsDetected:
["KNOWN_INSTALLED"]
במכשיר מותקנות רק אפליקציות שמזוהות על ידי Google Play או שנטענו מראש במחיצת המערכת על ידי יצרן המכשיר.
לא מופעלות אפליקציות שיובילו לתוצאות של לכידה, שליטה או שכבות-על.
appsDetected:
["KNOWN_INSTALLED",
"UNKNOWN_INSTALLED",
"UNKNOWN_CAPTURING"]
יש אפליקציות שהותקנו דרך Google Play או שהוטענו מראש במחיצת המערכת על ידי יצרן המכשיר.
יש אפליקציות אחרות שפועלות וההרשאות שלהן מופעלות, ואפשר להשתמש בהן כדי להציג את המסך או לצלם קלט ופלט אחרים.
appsDetected:
["KNOWN_INSTALLED",
"KNOWN_CAPTURING",
"UNKNOWN_INSTALLED",
"UNKNOWN_CONTROLLING"]
יש אפליקציות של Play או אפליקציות מערכת שפועלות עם הרשאות מופעלות, שאפשר להשתמש בהן כדי להציג את המסך או לצלם קלט ופלט אחרים.
יש גם אפליקציות אחרות שפועלות עם הרשאות מופעלות, שיכולות לשמש לשליטה במכשיר ולשליטה ישירה בקלט באפליקציה שלכם.
appAccessRiskVerdict: {} לא מתבצעת הערכה של הסיכון לגישה לאפליקציה כי לא מתקיימת דרישה נדרשת. לדוגמה, המכשיר לא היה מספיק מהימן.

בהתאם לרמת הסיכון, אתם יכולים להחליט אילו שילובים של תוצאות קביעה מקובלים כדי להמשיך, ואילו תוצאות קביעה יגרמו לכם לבצע פעולה. בקטע הקוד הבא מוצגת דוגמה לאימות שלא פועלות אפליקציות שיכולות לצלם את המסך או לשלוט באפליקציה שלכם:

Kotlin

val environmentDetails =
    JSONObject(payload).getJSONObject("environmentDetails")
val appAccessRiskVerdict =
    environmentDetails.getJSONObject("appAccessRiskVerdict")

if (appAccessRiskVerdict.has("appsDetected")) {
    val appsDetected = appAccessRiskVerdict.getJSONArray("appsDetected").toString()
    if (!appsDetected.contains("CAPTURING") && !appsDetected.contains("CONTROLLING")) {
        // Looks good!
    }
}

Java

JSONObject environmentDetails =
    new JSONObject(payload).getJSONObject("environmentDetails");
JSONObject appAccessRiskVerdict =
    environmentDetails.getJSONObject("appAccessRiskVerdict");

if (appAccessRiskVerdict.has("appsDetected")) {
    String appsDetected = appAccessRiskVerdict.getJSONArray("appsDetected").toString()
    if (!appsDetected.contains("CAPTURING") && !appsDetected.contains("CONTROLLING")) {
        // Looks good!
    }
}
תיקון של קביעות סיכון הגישה לאפליקציה

בהתאם לרמת הסיכון, אתם יכולים להחליט על אילו קביעות סיכון הגישה לאפליקציה אתם רוצים לפעול לפני שמאפשרים למשתמש להשלים בקשה או פעולה. אפשר להציג למשתמשים בקשות אופציונליות מ-Google Play אחרי בדיקת קביעת הסיכון לגישה לאפליקציה. אפשר להציג את האפשרות CLOSE_UNKNOWN_ACCESS_RISK כדי לבקש מהמשתמש לסגור אפליקציות לא מוכרות שגורמות לסיכון לגישה לאפליקציה, או להציג את האפשרות CLOSE_ALL_ACCESS_RISK כדי לבקש מהמשתמש לסגור את כל האפליקציות (מוכרות ולא מוכרות) שגורמות לסיכון לגישה לאפליקציה.

התוצאה של Play Protect

אחרי ההפעלה, השדה environmentDetails במטען הייעודי (payload) של Play Integrity API יכיל את קביעת התקינות של Play Protect:

"environmentDetails": {
  "playProtectVerdict": "NO_ISSUES"
}

playProtectVerdict יכול לקבל את אחד מהערכים הבאים:

NO_ISSUES
השירות Play Protect מופעל ולא נמצאו במכשיר בעיות באפליקציות.
NO_DATA
שירותי Play Protect מופעלים, אבל עדיין לא בוצעה סריקה. יכול להיות שהמכשיר או אפליקציית חנות Play אופסו לאחרונה.
POSSIBLE_RISK
שירותי Play Protect מושבתים.
MEDIUM_RISK
השירות Play Protect מופעל ומצא אפליקציות שעלולות להזיק שמותקנות במכשיר.
HIGH_RISK
Play Protect מופעל ומצא אפליקציות מסוכנות שמותקנות במכשיר.
UNEVALUATED

לא בוצעה הערכה של קביעת סטטוס הרישיון ב-Play Protect.

יכולות להיות לכך כמה סיבות, כולל:

  • המכשיר לא מספיק מהימן.
  • לחשבון המשתמש אין רישיון ל-Play.

הנחיות לשימוש בתוצאת הבדיקה של Play Protect

מערכת השרת העורפי של האפליקציה יכולה להחליט איך לפעול בהתאם לתוצאה, על סמך הסבילות שלכם לסיכון. ריכזנו כאן כמה הצעות ופעולות אפשריות של משתמשים:

NO_ISSUES
ההגנה של Play Protect מופעלת ולא נמצאו בעיות, לכן לא נדרשת פעולה מצד המשתמש.
POSSIBLE_RISK וגם NO_DATA
כשמקבלים את התוצאות האלה, צריך לבקש מהמשתמש לבדוק ש-Play Protect מופעל ושהוא ביצע סריקה. NO_DATA אמור להופיע רק במקרים נדירים.
MEDIUM_RISK וגם HIGH_RISK
בהתאם למידת הסיכון שאתם מוכנים לקחת, אתם יכולים לבקש מהמשתמש להפעיל את Play Protect ולפעול לפי האזהרות של Play Protect. אם המשתמש לא עומד בדרישות האלה, אפשר לחסום אותו מביצוע פעולות בשרת.