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

توضّح هذه الصفحة عملية تقديم طلبات عادية من واجهة برمجة التطبيقات للحصول على بيانات السلامة، والتي يمكن استخدامها في 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 تلقائيًا عدم إعادة استخدام كل رمز مميّز للتحقّق من السلامة عدة مرات. وستؤدي محاولة فك تشفير الرمز المميّز نفسه بشكل متكرر إلى ظهور بيانات فارغة.