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

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

نظرة عامة

الشكل 1. مخطّط تسلسلي يعرض التصميم العالي المستوى لواجهة برمجة التطبيقات 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));

Unity

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

Unreal Engine

// .h
void MyClass::OnPrepareIntegrityTokenCompleted(
  EStandardIntegrityErrorCode ErrorCode,
  UStandardIntegrityTokenProvider* Provider)
{
  // Check the resulting error code.
  if (ErrorCode == EStandardIntegrityErrorCode::StandardIntegrity_NO_ERROR)
  {
    // ...
  }
}

// .cpp
void MyClass::PrepareIntegrityToken()
{
  int64 CloudProjectNumber = ...

  // Create the Integrity Token Request.
  FPrepareIntegrityTokenRequest Request = { CloudProjectNumber };

  // Create a delegate to bind the callback function.
  FPrepareIntegrityOperationCompletedDelegate Delegate;

  // Bind the completion handler (OnPrepareIntegrityTokenCompleted) to the delegate.
  Delegate.BindDynamic(this, &MyClass::OnPrepareIntegrityTokenCompleted);

  // Initiate the prepare integrity token operation, passing the delegate to handle the result.
  GetGameInstance()
    ->GetSubsystem<UStandardIntegrityManager>()
    ->PrepareIntegrityToken(Request, Delegate);
}

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

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

Unity

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

Unreal Engine

// .h
void MyClass::OnRequestIntegrityTokenCompleted(
  EStandardIntegrityErrorCode ErrorCode,
  UStandardIntegrityToken* Response)
{
  // Check the resulting error code.
  if (ErrorCode == EStandardIntegrityErrorCode::StandardIntegrity_NO_ERROR)
  {
    // Get the token.
    FString Token = Response->Token;
  }
}

// .cpp
void MyClass::RequestIntegrityToken()
{
  UStandardIntegrityTokenProvider* Provider = ...

  // Prepare the UStandardIntegrityTokenProvider.

  // Request integrity token by providing a user action request hash. Can be called
  // several times for different user actions.
  FString RequestHash = ...;
  FStandardIntegrityTokenRequest Request = { RequestHash };

  // Create a delegate to bind the callback function.
  FStandardIntegrityOperationCompletedDelegate Delegate;

  // Bind the completion handler (OnRequestIntegrityTokenCompleted) to the delegate.
  Delegate.BindDynamic(this, &MyClass::OnRequestIntegrityTokenCompleted);

  // Initiate the standard integrity token request, passing the delegate to handle the result.
  Provider->Request(Request, Delegate);
}

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

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

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

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

بعد طلب بيان سلامة، تقدّم واجهة برمجة التطبيقات 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 (أو على نتيجة فارغة إذا كانت نتيجة متعدّدة القيم).

حلّ المشاكل المتعلّقة بقرار التحقّق من الأمان باستخدام طلب من Google Play (اختياري)

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

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

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