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

توضّح هذه الصفحة كيفية إرسال طلبات البيانات العادية من واجهة برمجة التطبيقات للحصول على أحكام السلامة التي تتوفّر على Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) أو الإصدارات الأحدث. يمكنك إرسال طلب عادي لواجهة برمجة التطبيقات للحصول على نتيجة سلامة كلما أرسل تطبيقك طلبًا إلى الخادم للتحقّق مما إذا كان التفاعل حقيقيًا.

نظرة عامة

مخطّط تسلسلي يعرض التصميم العام لواجهة برمجة التطبيقات Play Integrity API

يتألّف الطلب العادي من جزأين:

  • إعداد مقدّم رمز السلامة (إجراء لمرة واحدة): عليك الاتصال بواجهة برمجة التطبيقات Integrity API لإعداد مقدّم رمز السلامة قبل وقت طويل من الحاجة إلى الحصول على بيان السلامة. على سبيل المثال، يمكنك إجراء ذلك عند بدء تشغيل تطبيقك أو في الخلفية قبل الحاجة إلى إصدار حكم السلامة.
  • طلب رمز أمان (عند الطلب): عندما يُرسِل تطبيقك طلبًا إلى أحد الخوادم ويريد التأكّد من صحته، يطلب رمز أمان ويرسله إلى خادم الخلفية في تطبيقك لفك التشفير والتحقّق من صحته. بعد ذلك، يمكن لخادم الخلفية تحديد الإجراء الذي يجب اتّخاذه.

تحضير مقدّم رمز السلامة (إجراء لمرة واحدة):

  1. يطلب تطبيقك من مقدّم الرمز المميّز للسلامة رقم مشروعك على Google Cloud.
  2. يحتفظ تطبيقك بمزوّد رمز سلامة التوقيع في الذاكرة لإجراء المزيد من مكالمات التحقّق من الشهادة.

طلب رمز سلامة (عند الطلب):

  1. بالنسبة إلى إجراء المستخدم الذي يجب حمايته، يحسب تطبيقك التجزئة (باستخدام أي خوارزمية تجزئة مناسبة مثل SHA256) للطلب الذي سيتم إجراؤه.
  2. يطلب تطبيقك رمزًا مميزًا للسلامة، مع تمرير تجزئة الطلب.
  3. يتلقّى تطبيقك الرمز المميّز للسلامة الموقَّع والمشفَّر من واجهة برمجة التطبيقات Play Integrity API.
  4. يُرسِل تطبيقك رمز سلامة التطبيق إلى الخلفية.
  5. يُرسِل الجزء الخلفي من تطبيقك الرمز المميّز إلى خادم Google Play. يفكِّ خادم Google Play التشفير ويتحقق من البيان، ويرسل النتائج إلى واجهة برمجة التطبيقات في الخلفية لتطبيقك.
  6. يحدّد الجزء الخلفي من تطبيقك كيفية المتابعة استنادًا إلى الإشارات الواردة في بيانات أساسية الرمز المميّز.
  7. تُرسِل الخلفية في تطبيقك نتائج القرار إلى تطبيقك.

إعداد مقدّم الرمز المميّز لسلامة البيانات (إجراء لمرة واحدة)

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

يمكنك إعداد موفِّر الرمز المميَّز للسلامة:

  • عند تشغيل تطبيقك (أي عند التشغيل المُجرّد) إنّ عملية إعداد موفِّر الرموز المميّزة غير متزامنة، وبالتالي لن تؤثّر في وقت بدء التشغيل. سيكون هذا الخيار مناسبًا إذا كنت تخطّط لتقديم طلب بشأن سلامة التطبيق بعد إطلاقه بفترة قصيرة، مثلاً عندما يسجّل مستخدم الدخول أو ينضم لاعب إلى لعبة.
  • عند فتح تطبيقك (أي عند بدء التشغيل السريع) يُرجى العلم أنّ كل مثيل من التطبيق لا يمكنه إعداد رمز السلامة إلا 5 مرات في الدقيقة كحد أقصى.
  • في أي وقت في الخلفية عندما تريد إعداد الرمز المميّز مسبقًا قبل تقديم طلب بشأن نتيجة السلامة

لإعداد موفِّر رمز السلامة، اتّبِع الخطوات التالية:

  1. أنشئ StandardIntegrityManager، كما هو موضّح في الأمثلة التالية.
  2. أنشئ PrepareIntegrityTokenRequest، مع تقديم رقم مشروع Google Cloud من خلال طريقة setCloudProjectNumber().
  3. استخدِم المدير للاتصال برقم prepareIntegrityToken() مع تقديم PrepareIntegrityTokenRequest.

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));

الوحدة

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();
}

مدمجة مع المحتوى

/// 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);

حماية الطلبات من التلاعب (إجراء مقترَح)

عند التحقّق من إجراء أحد المستخدمين في تطبيقك باستخدام واجهة برمجة التطبيقات Play Integrity API، يمكنك الاستفادة من الحقل requestHash للحدّ من هجمات التلاعب. على سبيل المثال، قد تريد إحدى الألعاب إرسال نتيجة اللاعب إلى خادم الخلفي للعبة، ويريد خادمك التأكّد من عدم التلاعب بهذه النتيجة من قِبل خادم وكيل. تعرض واجهة برمجة التطبيقات Play Integrity API القيمة التي تحدّدها في الحقل requestHash ضمن استجابة السلامة الموقَّعة. بدون استخدام requestHash، سيتم ربط رمز أمان السلامة بالجهاز فقط، وليس بالطلب المحدّد، ما يفتح المجال لإمكانية حدوث هجوم. توضّح الخطوات التالية كيفية الاستفادة من الحقل requestHash بفعالية:

عند طلب بيان السلامة:

  • احتساب ملخّص لجميع مَعلمات الطلب ذات الصلة (مثل SHA256 لتسلسل طلب ثابت) من إجراء المستخدم أو طلب الخادم الجاري الحد الأقصى لعدد أحرف القيمة التي تم ضبطها في الحقل requestHash هو 500 بايت. أدرِج في requestHash أي بيانات طلب تطبيق مهمة أو ذات صلة بالإجراء الذي تفحصه أو تحميه. يتم تضمين الحقل requestHash في رمز السلامة حرفيًا، لذا قد تؤدي القيم الطويلة إلى زيادة حجم الطلب.
  • قدِّم الملخّص كحقل requestHash لواجهة برمجة التطبيقات Play Integrity API، واحصل على رمز السلامة.

عند تلقّي بيان سلامة:

  • فك ترميز الرمز المميّز للسلامة واستخرِج حقل requestHash.
  • احتساب ملخّص للطلب بالطريقة نفسها المتّبعة في التطبيق (مثل SHA256 لتسلسل طلب ثابت)
  • قارِن بين الملخّصات من جهة التطبيق ومن جهة الخادم. إذا لم يتطابقا، لن يكون الطلب جديرًا بالثقة.

طلب بيان السلامة (عند الطلب)

بعد إعداد مقدّم رمز السلامة، يمكنك البدء بطلب بيانات السلامة من Google Play. لإجراء ذلك، يُرجى إكمال الخطوات التالية:

  1. احصل على StandardIntegrityTokenProvider، كما هو موضّح أعلاه.
  2. أنشئ StandardIntegrityTokenRequest، مع تقديم تجزئة الطلب لمحاولة الإجراء الذي تريد حمايته من خلال دالة setRequestHash.
  3. استخدِم مقدّم رمز السلامة للاتصال بـ request()، مع تقديم StandardIntegrityTokenRequest.

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));

الوحدة

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();
}

مدمجة مع المحتوى

/// 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();

فك تشفير بيان السلامة والتحقّق منه

بعد طلب بيان السلامة، تقدّم واجهة برمجة التطبيقات Play Integrity API رمزًا مميّزًا للردّ مُشفَّرًا. للحصول على بيانات سلامة الجهاز، عليك فك تشفير رمز السلامة على خوادم Google. لإجراء ذلك، يُرجى إكمال الخطوات التالية:

  1. أنشئ حساب خدمة ضمن مشروع Google Cloud المرتبط بتطبيقك.
  2. على خادم تطبيقك، احصل على رمز الوصول من بيانات اعتماد حساب الخدمة باستخدام نطاق playintegrity، ثم قدِّم الطلب التالي:

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
    '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. اقرأ استجابة JSON.

الحمولة الناتجة هي رمز مميّز بنص عادي يحتوي على بيانات السلامة.

الحماية التلقائية من إعادة التشغيل

للحدّ من هجمات إعادة التشغيل، يضمن Google Play تلقائيًا عدم إمكانية إعادة استخدام كل رمز مميّز للحفاظ على السلامة عدة مرات. ستؤدي محاولة فك تشفير الرمز المميّز نفسه بشكل متكرّر إلى محو الأحكام. بالنسبة إلى الرموز المميّزة المحمية من إعادة التشغيل، يتم عرض الأحكام التي تم فك تشفيرها على النحو التالي:

  • سيكون قرار التعرّف على الجهاز فارغًا.
  • سيتم ضبط بيان التعرّف على التطبيق وبيان ترخيص التطبيق على UNEVALUATED.
  • سيتم ضبط أي من النتائج الاختيارية التي تم تفعيلها باستخدام Play Console على UNEVALUATED (أو على نتيجة فارغة إذا كانت نتيجة متعددة القيم).