بيانات السلامة

توضّح هذه الصفحة كيفية تفسير الحكم الذي تم إرجاعه بشأن السلامة والتعامل معه. سواء أرسلت طلبًا عاديًا أو كلاسيكيًا إلى واجهة برمجة التطبيقات، سيتم عرض بيانات السلامة بالتنسيق نفسه والمحتوى المشابه. تعرض بيانات سلامة الجهاز معلومات حول صحة الأجهزة والتطبيقات والحسابات. يمكن لخادم تطبيقك استخدام الحمولة الناتجة في بيان سلامة تم فك تشفيره والتحقّق منه لتحديد أفضل طريقة للمتابعة في إجراء أو طلب معيّن في تطبيقك.

تنسيق بيان السلامة الذي يتم عرضه

الحمولة هي ملف JSON بنص عادي وتحتوي على إشارات السلامة بالإضافة إلى المعلومات التي يقدّمها المطوّر.

في ما يلي البنية العامة للحِزمة:

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

عليك أولاً التأكّد من أنّ القيم في الحقل requestDetails تتطابق مع القيم الواردة في الطلب الأصلي قبل التحقّق من كل نتيجة سلامة. توضّح الأقسام التالية كل حقل بمزيد من التفصيل.

حقل تفاصيل الطلب

يحتوي الحقل requestDetails على معلومات حول الطلب، بما في ذلك المعلومات التي يقدّمها المطوّر في requestHash للطلبات العادية وnonce للطلبات الكلاسيكية.

بالنسبة إلى طلبات البيانات العادية من واجهة برمجة التطبيقات:

"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 من حمولة 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.
        ...
}

بالنسبة إلى طلبات البيانات من واجهة برمجة التطبيقات الكلاسيكية:

"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 من حمولة 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، تتضمّن تصنيفًا واحدًا أو أكثر يوضّح مدى قدرة الجهاز على فرض سلامة التطبيق. إذا لم يستوفِ الجهاز معايير أي من التصنيفات، سيتم حذف deviceRecognitionVerdict من الحقل deviceIntegrity.

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

يمكن أن يتضمّن deviceRecognitionVerdict تلقائيًا ما يلي:

MEETS_DEVICE_INTEGRITY
يعمل التطبيق على جهاز Android أصلي ومعتمد. في الإصدار 13 من نظام التشغيل Android والإصدارات الأحدث، تتوفّر شهادة تستند إلى الأجهزة تثبت أنّ برنامج إقلاع الجهاز مقفل وأنّ نظام التشغيل Android الذي تم تحميله هو صورة معتمَدة من الشركة المصنّعة للجهاز.
فارغ (قيمة فارغة)
يعمل التطبيق على جهاز يتضمّن علامات تشير إلى تعرُّضه للهجوم (مثل اعتراض طلبات البيانات من واجهة برمجة التطبيقات) أو اختراق نظامه (مثل تزويده بإذن الوصول إلى الجذر)، أو لا يعمل التطبيق على جهاز فعلي (مثل المحاكي الذي لا يجتاز عمليات التأكّد من السلامة في 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" على الكمبيوتر، يمكن أن يحتوي deviceRecognitionVerdict أيضًا على التصنيف التالي:

MEETS_VIRTUAL_INTEGRITY
يعمل التطبيق على محاكي Android متوافق مع "خدمات Google Play". يجتاز المحاكي عمليات التأكّد من سلامة النظام ويستوفي متطلّبات التوافق الأساسية مع نظام Android.

معلومات الجهاز الاختيارية وميزة "تذكُّر الجهاز"

في حال الموافقة على تلقّي تصنيفات إضافية في بيان السلامة، يمكن أن يحتوي deviceRecognitionVerdict على التصنيفات الإضافية التالية:

MEETS_BASIC_INTEGRITY
يعمل التطبيق على جهاز يجتاز عمليات التحقق الأساسية من سلامة النظام. يمكن قفل برنامج الإقلاع للجهاز أو إلغاء قفله، ويمكن أن تكون حالة التشغيل متحقَّقًا منها أو غير متحقَّق منها. قد لا يكون الجهاز معتمَدًا، وفي هذه الحالة، لا يمكن أن تقدّم Google أي ضمانات بشأن الأمان أو الخصوصية أو توافق التطبيقات. على الإصدار 13 من نظام التشغيل Android والإصدارات الأحدث، لا يتطلّب بيان 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، ننصحك أيضًا بمراعاة إصدار حزمة تطوير البرامج (SDK) لنظام التشغيل Android في الحقل deviceAttributes.

سيعرض جهاز واحد تصنيفات متعددة للجهاز في نتيجة تقييم سلامة الجهاز إذا تم استيفاء كل معايير التصنيف.

سمات الجهاز

يمكنك أيضًا الموافقة على تلقّي سمات الجهاز التي توضّح إصدار حزمة تطوير البرامج (SDK) لنظام التشغيل Android المثبّت على الجهاز. في المستقبل، قد يتم توسيع نطاقها لتشمل سمات أخرى للأجهزة.

قيمة إصدار حزمة SDK هي رقم إصدار حزمة SDK لنظام التشغيل Android المحدّد في 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 بين الأوضاع ويمكن أن تتضمّن إحدى القيم التالية:

مستوى نشاط الجهاز الحديث طلبات الرموز المميزة للسلامة العادية لواجهة برمجة التطبيقات على هذا الجهاز خلال آخر ساعة لكل تطبيق طلبات الرمز المميز للسلامة في واجهة برمجة التطبيقات القديمة على هذا الجهاز خلال الساعة الأخيرة لكل تطبيق
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: استرجِع تواريخ كتابة البتات بالتوقيت العالمي المنسَّق بدقة تصل إلى السنة والشهر. سيتم تعديل تاريخ كتابة بت الاستدعاء في كل مرة يتم فيها ضبط قيمة البت على 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!
}

حقل تفاصيل البيئة

يمكنك أيضًا الموافقة على تلقّي إشارات إضافية عن البيئة. تتيح لك ميزة "احتمالية اختراق التطبيق" معرفة ما إذا كانت هناك تطبيقات أخرى قيد التشغيل يمكنها أخذ لقطات للشاشة أو عرض المحتوى على سطح الصفحة أو التحكم في الجهاز. يتيح لك بيان "Google Play للحماية" معرفة ما إذا كانت خدمة "Google Play للحماية" مفعَّلة على الجهاز وإذا كان قد تم اكتشاف برامج ضارة معروفة.

إذا وافقت على تلقّي بيان "مخاطر الوصول إلى التطبيق" أو بيان "Play للحماية" في Google Play Console، سيتضمّن الردّ من واجهة برمجة التطبيقات الحقل environmentDetails. يمكن أن يحتوي الحقل environmentDetails على قيمتين، appAccessRiskVerdict وplayProtectVerdict.

بيان خطورة الوصول إلى التطبيق

عند تفعيل هذه الميزة، سيتضمّن حقل environmentDetails في حمولة 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 فارغًا. وقد يحدث ذلك لعدة أسباب، بما فيها ما يلي:

  • الجهاز غير موثوق بالقدر الكافي
  • شكل الجهاز ليس هاتفًا أو جهازًا لوحيًا أو جهازًا قابلاً للطي.
  • لا يعمل الجهاز بالإصدار 6 من نظام التشغيل Android (المستوى 23 لواجهة برمجة التطبيقات) أو إصدار أحدث.
  • لا يتعرّف Google Play على إصدار التطبيق المثبّت على الجهاز
  • إصدار "متجر Google Play" على الجهاز قديم.
  • لا يتضمّن حساب المستخدم ترخيصًا على Play.
  • تم استخدام طلب عادي مع المَعلمة verdictOptOut.
  • تم استخدام طلب عادي مع إصدار من مكتبة Play Integrity API لا يتيح بعد ميزة "احتمالية اختراق التطبيق" للطلبات العادية.

تستبعد ميزة "مخاطر الوصول إلى التطبيقات" تلقائيًا خدمات تسهيل الاستخدام التي تم التحقّق منها والتي خضعت لمراجعة محسّنة بشأن تسهيل الاستخدام على Google Play (تم تثبيتها من أي متجر تطبيقات على الجهاز). تعني الحالة "مستبعد" أنّ خدمات تسهيل الاستخدام التي تم التحقّق منها والتي تعمل على الجهاز لن تعرض ردًا بشأن أخذ لقطات للشاشة أو التحكّم في الجهاز أو عرض محتوى على السطح في نتيجة تقييم احتمالية اختراق التطبيق. لطلب مراجعة محسّنة لتطبيق تسهيل الاستخدام على Google Play، عليك نشره على Google Play والتأكّد من ضبط العلامة isAccessibilityTool على القيمة "صحيح" في بيان تطبيقك، أو يمكنك طلب إجراء مراجعة.

أمثلة على بيانات خطورة الوصول إلى التطبيق

يعرض الجدول التالي بعض الأمثلة على نتائج تقييم مخاطر الوصول إلى التطبيقات ومعناها (لا يتضمّن هذا الجدول كل النتائج المحتملة):

مثال على ردّ بيان خطورة الوصول إلى التطبيق التفسير
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 للحماية"

بعد تفعيل هذه الميزة، سيتضمّن الحقل environmentDetails في حمولة Play Integrity API بيان "Google Play للحماية":

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

يمكن أن تتضمّن playProtectVerdict إحدى القيم التالية:

NO_ISSUES
تكون خدمة "Play للحماية" مفعّلة ولم ترصد أي مشاكل في التطبيقات على الجهاز.
NO_DATA
تم تفعيل خدمة "Play للحماية" ولكن لم يتم إجراء أي عملية فحص حتى الآن. ربما تمت إعادة ضبط الجهاز أو تطبيق "متجر Play" مؤخرًا.
POSSIBLE_RISK
تم إيقاف "Play للحماية".
MEDIUM_RISK
تكون خدمة "Play للحماية" مفعَّلة وقد رصدت تطبيقات مثبّتة على الجهاز يُحتمَل أن تكون ضارّة.
HIGH_RISK
تكون خدمة "Play للحماية" مفعَّلة وقد رصدت تطبيقات خطيرة مثبّتة على الجهاز.
UNEVALUATED

لم يتم تقييم بيان "Play للحماية".

وقد يحدث ذلك لعدة أسباب، بما فيها ما يلي:

  • الجهاز غير موثوق بالقدر الكافي
  • لا يتضمّن حساب المستخدم ترخيصًا على Play.

إرشادات حول استخدام بيان "Google Play للحماية"

يمكن لخادم الخلفية في تطبيقك اتخاذ الإجراءات المناسبة بناءً على بيان السلامة ومستوى تحمّل تطبيقك للمخاطر. في ما يلي بعض الاقتراحات والإجراءات المحتملة التي يمكن للمستخدم اتّخاذها:

NO_ISSUES
تكون خدمة "Play للحماية" مفعَّلة ولم ترصد أي مشاكل، لذا ليس على المستخدم اتخاذ أي إجراء.
POSSIBLE_RISK وNO_DATA
عند تلقّي هذه النتائج، اطلب من المستخدم التأكّد من أنّ خدمة "Play للحماية" مفعّلة وأنّها أجرت عملية فحص. يجب ألا يظهر NO_DATA إلا في حالات نادرة.
MEDIUM_RISK وHIGH_RISK
بناءً على مستوى تحمّلك للمخاطر، يمكنك أن تطلب من المستخدم تشغيل "Play للحماية" واتّخاذ إجراء بشأن تحذيرات "Play للحماية". إذا لم يستوفِ المستخدم هذه المتطلبات، يمكنك حظره من تنفيذ إجراء الخادم.