Standart API isteği yapma

Bu sayfada, Android 5.0 (API düzeyi 21) veya sonraki sürümlerde desteklenen entegrasyon kararları için standart API isteklerinde bulunma açıklanmaktadır. Uygulamanız sunucu çağrısı yaptığında etkileşimin gerçek olup olmadığını kontrol etmek için entegrasyon kararı için standart bir API isteğinde bulunabilirsiniz.

Genel bakış

Play Integrity API'nin üst düzey tasarımını gösteren sıra şeması

Standart bir istek iki bölümden oluşur:

  • Bütünlük jetonu sağlayıcıyı hazırlama (bir kereye mahsus): Bütünlük jetonu sağlayıcıyı, entegrasyon kararı size ulaşmadan çok önce hazırlamak için Integrity API'yi çağırmanız gerekir. Örneğin, uygulamanız başlatıldığında veya entegrasyon kararı istenmeden önce arka planda bunu yapabilirsiniz.
  • Bütünlük jetonu isteme (isteğe bağlı): Uygulamanız, gerçek olup olmadığını kontrol etmek istediğiniz sunucu isteğinde bulunduğunda, bütünlük jetonu ister ve şifre çözme ile doğrulama için bu jetonu uygulamanızın arka uç sunucusuna gönderirsiniz. Daha sonra arka uç sunucunuz ne yapacağına karar verebilir.

Bütünlük jetonu sağlayıcıyı hazırlama (tek seferlik):

  1. Uygulamanız, bütünlük jetonu sağlayıcıyı Google Cloud proje numaranızla çağırıyor.
  2. Uygulamanız, daha ayrıntılı onay kontrolü çağrıları için bütünlük jetonu sağlayıcıyı bellekte tutar.

Bütünlük jetonu isteme (isteğe bağlı):

  1. Uygulamanız, korunması gereken kullanıcı işlemi için yapılacak isteğin karmasını hesaplar (SHA256 gibi uygun bir karma algoritması kullanarak).
  2. Uygulamanız, istek karmasını ileterek bir bütünlük jetonu istiyor.
  3. Uygulamanız, Play Integrity API'den imzalanmış ve şifrelenmiş bütünlük jetonunu alır.
  4. Uygulamanız, bütünlük jetonunu uygulamanızın arka ucuna iletir.
  5. Uygulamanızın arka ucu, jetonu bir Google Play sunucusuna gönderir. Google Play sunucusu kararının şifresini çözüp sonuçları doğrular ve sonuçları uygulamanızın arka ucuna döndürür.
  6. Uygulamanızın arka ucu, jeton yükündeki sinyallere göre nasıl ilerleyeceğine karar verir.
  7. Uygulamanızın arka ucu, karar sonuçlarını uygulamanıza gönderir.

Bütünlük jetonu sağlayıcıyı hazırlama (tek seferlik)

Google Play'den entegrasyon kararı için standart bir istekte bulunmadan önce bütünlük jetonu sağlayıcıyı hazırlamanız (veya "ısıtmanız") gerekir. Bu sayede Google Play, entegrasyon kararı isteğinde bulunduğunuzda kritik yoldaki gecikmeyi azaltmak için cihazda kısmi onay bilgilerini akıllı bir şekilde önbelleğe alabilir. Jeton sağlayıcıyı tekrar hazırlamak, daha az kaynak ağırlıklı bütünlük kontrollerini tekrarlamanın bir yoludur. Bu sayede, talep ettiğiniz bir sonraki entegrasyon kararı daha güncel olur.

Bütünlük jetonu sağlayıcıyı hazırlayabilirsiniz:

  • Uygulamanız başlatıldığında (ör. baştan başlatmada). Jeton sağlayıcıyı hazırlamak eşzamansız bir işlemdir ve bu nedenle başlatma süresini etkilemez. Bir kullanıcı oturum açtığında veya oyuncu oyuna katıldığında uygulama kullanıma sunulduktan kısa bir süre sonra entegrasyon kararı isteğinde bulunmayı planlıyorsanız bu seçenek işe yarar.
  • Uygulamanız açıldığında (ör. hazır durumda başlatma sırasında). Ancak her uygulama örneğinin, bütünlük jetonunu dakikada en fazla 5 kez hazırlayabileceğini unutmayın.
  • Arka planda herhangi bir zamanda, entegrasyon kararı isteğinden önce jetonu hazırlamak istediğinizde.

Bütünlük jetonu sağlayıcıyı hazırlamak için aşağıdakileri yapın:

  1. Aşağıdaki örneklerde gösterildiği gibi bir StandardIntegrityManager oluşturun.
  2. Google Cloud proje numarasını setCloudProjectNumber() yöntemiyle sağlayarak bir PrepareIntegrityTokenRequest oluşturun.
  3. PrepareIntegrityTokenRequest sağlayarak prepareIntegrityToken() numarasını aramak için yöneticiyi kullanın.

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

Üçlü

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

Yerel

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

İstekleri, izinsiz değişikliklere karşı koru (önerilir)

Uygulamanızda bir kullanıcının yaptığı işlemleri Play Integrity API ile kontrol ederken, kurcalama saldırılarına karşı riski azaltmak için requestHash alanından yararlanabilirsiniz. Örneğin, bir oyun, oyuncunun puanını oyunun arka uç sunucusuna bildirmek isteyebilir ve sunucunuz bu puanın bir proxy sunucu tarafından değiştirilmediğinden emin olmak isteyebilir. Play Integrity API, requestHash alanında ayarladığınız değeri imzalı bütünlük yanıtının içinde döndürür. requestHash olmadan bütünlük jetonu yalnızca cihaza bağlanır ancak belirli bir isteğe bağlı olmaz. Bu da saldırı olasılığını artırır. Aşağıdaki talimatlarda, requestHash alanının etkili bir şekilde nasıl kullanılacağı açıklanmaktadır:

Entegrasyon kararı istediğinizde:

  • Gerçekleşen kullanıcı işleminden veya sunucu isteğinden ilgili tüm istek parametrelerinin (ör. kararlı istek serileştirmesinin SHA256) özetini hesaplayın. requestHash alanında ayarlanan değerin maksimum uzunluğu 500 bayttır. Kontrol ettiğiniz veya koruduğunuz işlemle alakalı veya önemli olan tüm uygulama isteği verilerini requestHash bölümüne ekleyin. requestHash alanı, bütünlük jetonuna tam olarak dahil edildiğinden uzun değerler istek boyutunu artırabilir.
  • Özeti Play Integrity API'ye requestHash alanı olarak sağlayın ve bütünlük jetonunu edinin.

Entegrasyon kararı aldığınızda:

  • Bütünlük jetonunun kodunu çözün ve requestHash alanını çıkarın.
  • İsteğin özetini uygulamadakiyle aynı şekilde (ör. kararlı istek serileştirmesinin SHA256'sı) hesaplayın.
  • Uygulama tarafı ve sunucu tarafı özetlerini karşılaştırın. Bunlar eşleşmezse istek güvenilir değildir.

Entegrasyon kararı isteme (isteğe bağlı)

Bütünlük jetonu sağlayıcıyı hazırladıktan sonra Google Play'den entegrasyon kararı istemeye başlayabilirsiniz. Bunun için aşağıdaki adımları uygulayın:

  1. Yukarıda gösterildiği gibi bir StandardIntegrityTokenProvider edinin.
  2. setRequestHash yöntemi ile korumak istediğiniz kullanıcı işleminin istek karmasını sağlayarak bir StandardIntegrityTokenRequest oluşturun.
  3. StandardIntegrityTokenRequest sağlayarak request() çağrısı yapmak için bütünlük jetonu sağlayıcıyı kullanın.

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

Üçlü

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

Yerel

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

Entegrasyon kararının şifresini çözün ve doğrulayın

Entegrasyon kararı istedikten sonra Play Integrity API şifrelenmiş bir yanıt jetonu sağlar. Cihaz entegrasyon kararlarını almak için Google'ın sunucularında bütünlük jetonunun şifresini çözmeniz gerekir. Bunun için aşağıdaki adımları uygulayın:

  1. Uygulamanıza bağlı Google Cloud projesinde bir hizmet hesabı oluşturun.
  2. Uygulamanızın sunucusunda, playintegrity kapsamını kullanarak hizmet hesabı kimlik bilgilerinizden erişim jetonunu getirin ve aşağıdaki isteği yapın:

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
    '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. JSON yanıtını okuyun.

Ortaya çıkan yük, bütünlük kararlarını içeren bir düz metin jetondur.

Otomatik tekrar oynatma koruması

Google Play, tekrar oynatma saldırılarını azaltmak için her bir bütünlük jetonunun birçok kez yeniden kullanılamamasını otomatik olarak sağlar. Aynı jetonun şifresini tekrar tekrar çözmeyi denerseniz boş sonuçlar alabilirsiniz.