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

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

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