בדף הזה מוסבר איך לפרש את פסק הדין לגבי היושרה שמוחזר ואיך לעבוד איתו. לא משנה אם שולחים בקשת API רגילה או קלאסית, קביעת התקינות מוחזרת באותו פורמט עם תוכן דומה. התוצאה של בדיקת התקינות מעבירה מידע על התוקף של מכשירים, אפליקציות וחשבונות. השרת של האפליקציה יכול להשתמש במטען הייעודי (payload) שמתקבל, אחרי שהוא מפוענח ומאומת, כדי לקבוע מהי הדרך הטובה ביותר להמשיך עם פעולה או בקשה מסוימת באפליקציה.
פורמט קביעת התקינות שמוחזר
המטען הייעודי הוא JSON בטקסט פשוט ומכיל אותות יושרה לצד מידע שסופק על ידי המפתח.
המבנה הכללי של המטען הייעודי (payload) הוא כזה:
{
"requestDetails": { ... },
"accountDetails": { ... },
"appIntegrity": { ... },
"deviceIntegrity": { ... },
"environmentDetails": { ... }
}
אין ערובה לסדר השדות במטען הייעודי (payload) בפורמט JSON. לפני שבודקים כל פסיקה לגבי תקינות, צריך לוודא שהערכים בשדה requestDetails תואמים לערכים בבקשה המקורית. בקטעים הבאים מתואר כל שדה בפירוט.
שדה פרטי הבקשה
השדה requestDetails מכיל מידע על הבקשה, כולל מידע שהמפתח סיפק בשדה requestHash עבור בקשות רגילות ובשדה nonce עבור בקשות קלאסיות.
לגבי בקשות standard 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. ... }
השדה של פרטי החשבון
השדה 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! }
שדה מהימנות האפליקציה
השדה 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! }
אפשר גם לבדוק את שם חבילת ה-APK, את גרסת האפליקציה ואת האישורים של האפליקציה באופן ידני.
שדה תקינות המכשיר
השדה 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) או לפריצה למערכת (למשל, רוט), או שהאפליקציה לא פועלת במכשיר פיזי (למשל, אמולטור שלא עובר את בדיקות היושרה של 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 OS ותיקון של מחיצת הספק. - ב-Android 12 ומטה, כדי לקבל את התוצאה
MEETS_STRONG_INTEGRITYנדרש רק אימות של תקינות ההפעלה ברמת החומרה, ולא נדרש שהמכשיר יעבור עדכון אבטחה עדכני. לכן, כשמשתמשים ב-MEETS_STRONG_INTEGRITY, מומלץ לקחת בחשבון גם את גרסת Android SDK בשדהdeviceAttributes.
- ב-Android 13 ומעלה, כדי לקבל את התוצאה
מכשיר יחיד יחזיר כמה תוויות מכשיר בתוצאת בדיקת תקינות המכשיר אם כל הקריטריונים של התווית מתקיימים.
מאפייני המכשיר
אפשר גם להביע הסכמה לשיתוף מאפייני המכשיר, שכוללים את גרסת Android SDK של מערכת ההפעלה Android שפועלת במכשיר. גרסת Android SDK שימושית להבחנה בין מכשירים עם Android 13 ומעלה לבין מכשירים עם גרסאות Android SDK נמוכות יותר, כחלק מאסטרטגיית אכיפה מדורגת. יכול להיות שבעתיד נרחיב את התכונה הזו ונוסיף לה מאפיינים נוספים של מכשירים.
ערך גרסת ה-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 שונות בין המצבים, והערך יכול להיות אחד מהערכים הבאים:
| רמת הפעילות במכשיר מהזמן האחרון | בקשות לטוקן תקינות של Standard API במכשיר הזה בשעה האחרונה לכל אפליקציה | בקשות לטוקן תקינות של API קלאסי במכשיר הזה בשעה האחרונה לכל אפליקציה |
|---|---|---|
LEVEL_1 (הכי נמוך) |
10 או פחות | 5 או פחות |
LEVEL_2 |
בין 11 ל-25 | בין 6 ל-10 |
LEVEL_3 |
בין 26 ל-50 | בין 11 ל-15 |
LEVEL_4 (הכי גבוה) |
יותר מ-50 | יותר מ-15 |
UNEVALUATED |
הפעילות במכשיר מהזמן האחרון לא הוערכה. זה יכול לקרות בגלל:
|
|
זכירת נתונים שמשויכים למכשיר (בטא)
אתם יכולים גם להפעיל את האפשרות שחזור נתונים לפי מכשיר, שמאפשרת לכם לאחסן נתונים מסוימים בהתאמה אישית, לפי מכשיר, ולשחזר אותם באופן מהימן כשמתקינים את האפליקציה מחדש במכשיר מאוחר יותר. אחרי שמבקשים טוקן יושרה, מבצעים הפעלה נפרדת משרת לשרת כדי לשנות את ערכי ההחזרה של המכשיר עבור מכשיר ספציפי.
אם תצטרפו ל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": {}
}
}
שדה פרטי הסביבה
אפשר גם להסכים לשיתוף אותות נוספים לגבי הסביבה. האות 'סיכון לגישה לאפליקציה' מאפשר לאפליקציה לדעת אם יש אפליקציות פועלות אחרות שיכולות לשמש לצילום המסך, להצגת שכבות-על או לשליטה במכשיר. הסטטוס של 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. אם המשתמש לא עומד בדרישות האלה, אפשר לחסום אותו מביצוע פעולות בשרת.