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 hakkında bilgi verilmektedir. Uygulamanız etkileşimin gerçek olup olmadığını kontrol etmek için sunucu çağrısı yaptığında, bütünlük kararı için standart bir API isteği gönderebilirsiniz.

Genel bakış

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

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

  • Bütünlük jetonu sağlayıcısını hazırlama (bir kerelik): Entegrasyon kararını almadan önce bütünlük jetonu sağlayıcısını hazırlamak için Integrity API'yi çağırmanız gerekir. Örneğin, entegrasyon kararı gerekli olmadan önce uygulamanız başlatılırken veya arka planda bunu yapabilirsiniz.
  • Bütünlük jetonu isteme (talep üzerine): Uygulamanız, orijinal olup olmadığını kontrol etmek istediğiniz bir sunucu isteğinde bulunduğunda bütünlük jetonu ister, şifre çözme ve doğrulama için bunu uygulamanızın arka uç sunucusuna gönderirsiniz. Ardından arka uç sunucunuz ne yapacağına karar verebilir.

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

  1. Uygulamanız, Google Cloud proje numaranızla bütünlük jetonu sağlayıcısını çağırır.
  2. Uygulamanız diğer onay kontrolü çağrıları için bütünlük jetonu sağlayıcısını bellekte tutar.

Bütünlük jetonu isteme (talep üzerine):

  1. Uygulamanız, korunması gereken kullanıcı işlemi için yapılacak isteğin karmasını (SHA256 gibi uygun bir karma algoritması kullanarak) hesaplar.
  2. Uygulamanız, istek karmasını ileterek bir bütünlük jetonu ister.
  3. Uygulamanız, Play Integrity API'den imzalı 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ı çözer ve doğrular, sonuçları uygulamanızın arka ucuna döndürür.
  6. Uygulamanızın arka ucu, jeton yükünde yer alan sinyallere göre nasıl devam edeceğ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 standart bir entegrasyon kararı isteğinde bulunmadan önce, bütünlük jetonu sağlayıcıyı hazırlamanız (veya "hazırlamanız") gerekir. Bu sayede Google Play, bütünlük kararı isteğinde bulunduğunuzda kritik yoldaki gecikmeyi azaltmak için cihazdaki kısmi onay bilgilerini akıllı bir şekilde önbelleğe alabilir. Jeton sağlayıcıyı tekrar hazırlamak, daha az kaynak kullanan yoğun bütünlük kontrollerini tekrarlamanın bir yoludur. Bu sayede, daha güncel olmasını istediğiniz bir sonraki entegrasyon kararını alırsınız.

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

  • Uygulamanızın başlatılması (ör. baştan başlatma). Jeton sağlayıcıyı hazırlamak eşzamansız olduğundan başlangıç süresini etkilemez. Örneğin, bir kullanıcı oturum açtıktan veya bir oyuncu bir oyuna katıldığında, uygulama kullanıma sunulduktan kısa bir süre sonra bütünlük kararı isteğinde bulunmayı düşünüyorsanız bu seçenek faydalı olur.
  • Uygulamanız açıldığında (ör. hazır durumda başlatmada). Ancak her uygulama örneğinin bütünlük jetonunu dakikada en fazla 5 kez hazırlayabileceğini unutmayın.
  • Bütünlük kararı isteğinden önce jetonu hazırlamak için arka planda istediğiniz zaman kullanabilirsiniz.

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öntemini kullanarak sağlayarak bir PrepareIntegrityTokenRequest oluşturun.
  3. prepareIntegrityToken() numarasını aramak için yöneticiyi kullanın ve PrepareIntegrityTokenRequest öğesini sağlayı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 Play Integrity API ile bir kullanıcı işlemini kontrol ederken, izinsiz değişiklik saldırılarına karşı riskleri azaltmak için requestHash alanından yararlanabilirsiniz. Örneğin, bir oyun oyuncunun skorunu oyunun arka uç sunucusuna bildirmek isteyebilir ve sunucunuz bu skorun 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 kullanılmadığında bütünlük jetonu yalnızca cihaza bağlı olacak ancak belirli bir isteğe bağlı olmayacak; bu da saldırı olasılığını artırır. Aşağıdaki talimatlarda requestHash alanından etkili bir şekilde nasıl yararlanılacağı açıklanmaktadır:

Entegrasyon kararı talep ettiğinizde:

  • Gerçekleşen kullanıcı işlemi veya sunucu isteğiyle ilgili tüm istek parametrelerinin (ör. kararlı bir istek serileştirmesinin SHA256'sı) özetini hesaplayın. requestHash alanında ayarlanan değerin maksimum uzunluğu 500 bayttır. Kontrol ettiğiniz veya koruduğunuz işlemle ilgili önemli ya da alakalı olan tüm uygulama isteği verilerini requestHash bölümüne ekleyin. requestHash alanı, bütünlük jetonunun kelimesi kelimesine 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 alın.

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 hesaplayın (ör. kararlı bir istek serileştirmesinin SHA256'sı).
  • Uygulama tarafı ve sunucu tarafı özetlerini karşılaştırın. Eşleşmiyorlarsa 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 bütünlük 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. Korumak istediğiniz kullanıcı işleminin istek karmasını setRequestHash yöntemiyle sağlayarak bir StandardIntegrityTokenRequest oluşturun.
  3. request() yöntemini çağırmak için StandardIntegrityTokenRequest özelliğini sağlayarak 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ı istediğinizde Play Integrity API şifrelenmiş bir yanıt jetonu sağlar. Cihaz entegrasyon kararlarını almak için Google 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. Bu hesap oluşturma işlemi sırasında hizmet hesabınıza Hizmet Hesabı Kullanıcısı ve Hizmet Kullanımı Tüketicisi rollerini vermeniz gerekir.
  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ı içeren düz metin biçiminde bir jetondur.

Otomatik tekrar oynatma koruması

Google Play, tekrar oynatma saldırılarını azaltmak için her bütünlük jetonunun birçok kez yeniden kullanılmasını otomatik olarak sağlar. Aynı jetonun tekrar tekrar şifresini çözmeye çalışmak, sonuçtan boş sonuçlar çıkmasına neden olur.