Klasik API isteği yapma

Yalnızca geliştiricilerin çoğuna uygun olan standart API istekleri yapmayı planlıyorsanız bütünlük kararlarına geçebilirsiniz. Bu sayfada, Android 4.4 (API düzeyi 19) veya sonraki sürümlerde desteklenen, entegrasyon kararları için klasik API isteklerinde bulunma açıklanmaktadır.

Dikkat edilmesi gereken noktalar

Standart ve klasik istekleri karşılaştırma

Standart istekler veya klasik istekler gönderebilir ya da uygulamanızın güvenliği ve kötüye kullanımı önleme ihtiyaçlarına bağlı olarak bu ikisinin bir kombinasyonunu gönderebilirsiniz. Standart istekler tüm uygulamalar ve oyunlar için uygundur. Herhangi bir işlemin veya sunucu çağrısının gerçekliğini kontrol etmek için kullanılabilir. Aynı zamanda, tekrar oynanabilirliğe ve veri sızıntısına karşı koruma sağlama yetkisini Google Play'e verir. Klasik isteklerin oluşturulması daha pahalıdır. Hırsızlığa ve belirli saldırı türlerine karşı koruma sağlamak için bunları doğru şekilde uygulama sorumluluğu size aittir. Klasik istekler, standart isteklerden daha seyrek yapılmalıdır (örneğin, son derece değerli veya hassas bir işlemin gerçek olup olmadığını kontrol etmek için ara sıra gönderilen tek seferlik).

Aşağıdaki tabloda, iki istek türü arasındaki temel farklar vurgulanmaktadır:

Standart API isteği Klasik API isteği
Ön koşullar
Minimum Android SDK sürümü gerekli Android 5.0 (API düzeyi 21) veya sonraki sürümler Android 4.4 (API düzeyi 19) veya sonraki sürümler
Google Play şartları Google Play Store ve Google Play Hizmetleri Google Play Store ve Google Play Hizmetleri
Entegrasyon ayrıntıları
API ısınma gerekli ✔️ (birkaç saniye)
Normal istek gecikmesi Birkaç yüz milisaniye Birkaç saniye
Olası istek sıklığı Sık (herhangi bir işlem veya istek için talep üzerine kontrol) Nadir (en yüksek değere sahip işlemler veya en hassas istekler için tek seferlik kontrol)
Engelleme Çoğu ısınma süresi 10 saniyeden kısadır ancak bir sunucu çağrısı içerdiğinden uzun bir zaman aşımı süresi (ör. 1 dakika) önerilir. Karar istekleri istemci tarafında gerçekleşir Çoğu istek 10 saniyeden kısa sürer ancak bir sunucu çağrısı içerdiğinden uzun bir zaman aşımı süresi önerilir (ör. 1 dakika)
Integrity karar jetonu
Cihaz, uygulama ve hesap ayrıntılarını içerir
Jeton önbelleğe alma Google Play tarafından korunan cihaz üzerinde önbelleğe alma Önerilmez
Google Play sunucusu üzerinden jetonun şifresini çözün ve doğrulayın
Tipik şifre çözme sunucudan sunucuya istek gecikmesi 10 sn. (üç dokuz kullanılabilirlik) 10 sn. (üç dokuz kullanılabilirlik)
Güvenli bir sunucu ortamında jetonun şifresini çözün ve yerel olarak jetonu doğrulayın
Jeton istemci tarafında şifre çözme ve jetonu doğrulama
Entegrasyon kararının güncelliği Google Play tarafından sunulan bazı otomatik önbelleğe alma ve yenileme işlemleri Tüm kararlar her istek için yeniden hesaplandı
Sınırlar
Uygulama başına günlük istek sayısı Varsayılan olarak 10.000 (artış istenebilir) Varsayılan olarak 10.000 (artış istenebilir)
Dakikada uygulama örneği başına istek sayısı Isınma: Dakikada 5
Bütünlük jetonları: Herkese açık sınır yok*
Bütünlük jetonları: Dakikada 5
Koruma
İzinsiz değişiklik ve benzer saldırılara karşı azaltın requestHash alanını kullan nonce alanını istek verilerine göre içerik bağlamayla kullanın
Tekrar oynatma ve benzer saldırılara karşı azaltın Google Play'den otomatik çözüm nonce alanını sunucu tarafı mantığıyla kullan

* Herkese açık sınırı olmayanlar da dahil olmak üzere tüm istekler, yüksek değerli kamu dışı savunma sınırlarına tabidir.

Nadiren klasik istekler gönderme

Bütünlük jetonu oluşturma işleminde zaman, veri ve pil kullanılır. Her uygulamanın bir günde yapabileceği maksimum klasik istek sayısı vardır. Bu nedenle, yalnızca standart bir isteğe ek garanti istediğinizde en yüksek değeri veya en hassas işlemlerin gerçek olduğunu kontrol etmek için klasik istekler göndermeniz gerekir. Yüksek sıklıklı veya düşük değerli işlemler için klasik istekler yapmamalısınız. Uygulama her ön plana çıktığında veya arka planda birkaç dakikada bir klasik istek göndermeyin ve aynı anda çok sayıda cihazdan arama yapmaktan kaçının. Çok fazla klasik istek çağrısı yapan bir uygulama, kullanıcıları yanlış uygulamalara karşı korumak için kısıtlanabilir.

Sonuçları önbelleğe almaktan kaçınma

Bir kararı önbelleğe almak, güvenilir olmayan bir ortamda iyi bir kararın yeniden kullanıldığı hırsızlık ve tekrar oynatma gibi saldırı riskini artırır. Bir klasik istek göndermeyi ve daha sonra kullanmak üzere önbelleğe almayı düşünüyorsanız bunun yerine isteğe bağlı standart bir istek gerçekleştirmeniz önerilir. Standart istekler cihazda bazı önbelleğe alma işlemlerini içerir ancak Google Play, tekrar oynatma ve veri hırsızlığı riskini azaltmak için ek koruma teknikleri kullanır.

Klasik istekleri korumak için tek seferlik rastgele sayı alanını kullanma

Play Integrity API, uygulamanızı tekrar oynatma ve izinsiz değişiklik saldırıları gibi belirli saldırılara karşı daha fazla korumak için kullanabileceğiniz nonce adlı bir alan sunar. Play Integrity API, bu alanda ayarladığınız değeri imzalı bütünlük yanıtının içinde döndürür. Uygulamanızı saldırılardan korumak için nonce oluşturma ile ilgili yönergeleri dikkatlice uygulayın.

Klasik istekleri eksponansiyel geri yükleme ile yeniden dene

Kararsız internet bağlantısı veya aşırı yüklenmiş cihaz gibi ortam koşulları, cihaz bütünlüğü kontrollerinin başarısız olmasına neden olabilir. Bu durum, güvenilir olan bir cihaz için etiket oluşturulmamasına yol açabilir. Bu senaryoları azaltmak için üstel geri yükleme özelliğine sahip bir yeniden deneme seçeneği ekleyin.

Genel bakış

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

Kullanıcı, uygulamanızda bütünlük kontrolüyle korumak istediğiniz yüksek değerli bir işlem gerçekleştirdiğinde aşağıdaki adımları uygulayın:

  1. Uygulamanızın sunucu tarafı arka ucu, benzersiz bir değer oluşturur ve istemci tarafı mantığına gönderir. Geri kalan adımlarda bu mantık "uygulamanız" olarak adlandırılır.
  2. Uygulamanız, yüksek değerli işleminizin benzersiz değerinden ve içeriğinden nonce oluşturur. Daha sonra, nonce öğesini ileterek Play Integrity API'yi çağırır.
  3. Uygulamanız, Play Integrity API'den imzalı ve şifrelenmiş bir karar alır.
  4. Uygulamanız, imzalı ve şifrelenmiş kararı uygulamanızın arka ucuna iletir.
  5. Uygulamanızın arka ucu, kararı bir Google Play sunucusuna gönderir. Google Play sunucusu kararın şifresini çözüp kararı doğrulayarak 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.

Tek seferlik rastgele sayı oluştur

Uygulamanızdaki bir işlemi Play Integrity API ile koruduğunuzda ortadaki kişi (PITM) saldırısı ve tekrarlama saldırıları gibi belirli saldırı türlerini hafifletmek için nonce alanından yararlanabilirsiniz. Play Integrity API, imzalı bütünlük yanıtının içinde bu alanda ayarladığınız değeri döndürür.

nonce alanında ayarlanan değer doğru şekilde biçimlendirilmelidir:

  • String
  • URL açısından güvenli
  • Base64 olarak kodlanır ve sarmalamaz
  • En az 16 karakter
  • Maksimum 500 karakter

Play Integrity API'de nonce alanını kullanmanın yaygın olarak kullanılan bazı yöntemleri aşağıda verilmiştir. nonce cihazından en güçlü korumayı elde etmek için aşağıdaki yöntemleri birleştirebilirsiniz.

İzinsiz olarak değiştirilmeye karşı koruma sağlamak için istek karması ekleyin

Klasik API isteğinde nonce parametresini, isteğin içeriğini izinsiz değişikliğe karşı korumak için standart API isteğindeki requestHash parametresine benzer şekilde kullanabilirsiniz.

Entegrasyon kararı istediğinizde:

  1. Gerçekleşen kullanıcı işleminden veya sunucu isteğinden tüm kritik istek parametrelerinin (ör. kararlı bir istek serileştirmesinde SHA256) bir özetini hesaplayın.
  2. nonce alanını hesaplanan özetin değerine ayarlamak için setNonce işlevini kullanın.

Entegrasyon kararı aldığınızda:

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

Tekrarlanan saldırılara karşı korumak için benzersiz değerler ekleyin.

Kötü amaçlı kullanıcıların Play Integrity API'den gelen önceki yanıtları yeniden kullanmasını önlemek için nonce alanını kullanarak her mesajı benzersiz şekilde tanımlayabilirsiniz.

Entegrasyon kararı istediğinizde:

  1. Kötü amaçlı kullanıcıların tahmin edemeyeceği şekilde genel olarak benzersiz bir değer elde edin. Örneğin, sunucu tarafında oluşturulan kriptografik olarak güvenli rastgele bir sayı, böyle bir değer veya oturum ya da işlem kimliği gibi önceden var olan bir kimlik olabilir. Daha basit ve daha az güvenli bir varyant, cihazda rastgele bir sayı oluşturmaktır. 128 bit veya daha büyük değerler oluşturmanızı öneririz.
  2. nonce alanını 1. adımdaki benzersiz değere ayarlamak için setNonce() komutunu çağırın.

Entegrasyon kararı aldığınızda:

  1. Bütünlük jetonunun kodunu çözüp doğrulayın ve nonce alanından benzersiz değeri alın.
  2. 1. adımdaki değer sunucuda oluşturulduysa alınan benzersiz değerin oluşturulan değerlerden biri olduğundan ve bu değerin ilk kez kullanıldığından emin olun (sunucunuzun, oluşturulan değerlerin kaydını uygun bir süre boyunca saklaması gerekir). Alınan benzersiz değer zaten kullanılmışsa veya kayıtta görünmüyorsa isteği reddedin
  3. Aksi takdirde, benzersiz değer cihazda oluşturulmuşsa alınan değerin ilk kez kullanıldığından emin olun (sunucunuzun uygun bir süre boyunca önceden görülen değerlerin kaydını tutması gerekir). Alınan benzersiz değer daha önce kullanılmışsa isteği reddedin.

İzinsiz değişiklik ve tekrar saldırılarına karşı iki korumayı bir arada kullanın (önerilir)

Aynı anda hem izinsiz değişiklik hem de tekrar saldırılarına karşı koruma sağlamak için nonce alanı kullanılabilir. Bunu yapmak için yukarıda açıklanan şekilde benzersiz değeri oluşturun ve isteğinize ekleyin. Daha sonra, benzersiz değeri karmanın bir parçası olarak eklediğinizden emin olarak istek karmasını hesaplayın. Bu iki yaklaşımı birleştiren bir uygulama aşağıdaki gibidir:

Entegrasyon kararı istediğinizde:

  1. Yüksek değerli işlemi kullanıcı başlatır.
  2. Bu işlem için, Tekrarlanan saldırılara karşı koruma sağlamak amacıyla benzersiz değerler ekleme bölümünde açıklandığı şekilde benzersiz bir değer elde edin.
  3. Korumak istediğiniz bir mesaj hazırlayın. 2. adımdaki benzersiz değeri mesaja ekleyin.
  4. Uygulamanız, korumak istediği mesajın bir özetini hesaplar. Bu özet, Kurmacaya karşı koruma sağlamak için istek karması ekleme bölümünde açıklanmıştır. Mesaj benzersiz değeri içerdiğinden, benzersiz değer karmanın bir parçasıdır.
  5. nonce alanını önceki adımdaki hesaplanan özete ayarlamak için setNonce() yöntemini kullanın.

Entegrasyon kararı aldığınızda:

  1. İstekten benzersiz değeri elde etme
  2. Bütünlük jetonunun kodunu çözüp doğrulayın ve nonce alanından özeti alın.
  3. İzinsiz değişikliğe karşı koruma sağlamak için bir istek karması ekleme bölümünde açıklandığı gibi, sunucu tarafında özeti yeniden hesaplayın ve bütünlük jetonundan alınan özetle eşleşip eşleşmediğini kontrol edin.
  4. Tekrarlanan saldırılara karşı koruma sağlamak için benzersiz değerleri dahil edin bölümünde açıklandığı gibi, benzersiz değerin geçerliliğini kontrol edin.

Aşağıdaki sıra şemasında, bu adımlar sunucu tarafı bir nonce ile gösterilmektedir:

Hem izinsiz değişikliğe hem de tekrarlama saldırılarına karşı nasıl korunacağını gösteren sıra diyagramı

Entegrasyon kararı iste

nonce oluşturduktan sonra Google Play'den entegrasyon kararı isteyebilirsiniz. Bunun için aşağıdaki adımları uygulayın:

  1. Aşağıdaki örneklerde gösterildiği gibi bir IntegrityManager oluşturun.
  2. nonce öğesini ilişkili derleyicide setNonce() yöntemiyle sağlayarak bir IntegrityTokenRequest oluşturun. Google Play dışında özel olarak dağıtılan uygulamalar ve SDK'lar da setCloudProjectNumber() yöntemiyle Google Cloud proje numaralarını belirtmek zorundadır. Google Play'deki uygulamalar Play Console'daki bir Cloud projesine bağlıdır ve istekte Cloud projesi numarasının ayarlanması gerekmez.
  3. IntegrityTokenRequest sağlayarak requestIntegrityToken() numarasını aramak için yöneticiyi kullanın.

Kotlin

// Receive the nonce from the secure server.
val nonce: String = ...

// Create an instance of a manager.
val integrityManager =
    IntegrityManagerFactory.create(applicationContext)

// Request the integrity token by providing a nonce.
val integrityTokenResponse: Task<IntegrityTokenResponse> =
    integrityManager.requestIntegrityToken(
        IntegrityTokenRequest.builder()
             .setNonce(nonce)
             .build())

Java

import com.google.android.gms.tasks.Task; ...

// Receive the nonce from the secure server.
String nonce = ...

// Create an instance of a manager.
IntegrityManager integrityManager =
    IntegrityManagerFactory.create(getApplicationContext());

// Request the integrity token by providing a nonce.
Task<IntegrityTokenResponse> integrityTokenResponse =
    integrityManager
        .requestIntegrityToken(
            IntegrityTokenRequest.builder().setNonce(nonce).build());

Üçlü

IEnumerator RequestIntegrityTokenCoroutine() {
    // Receive the nonce from the secure server.
    var nonce = ...

    // Create an instance of a manager.
    var integrityManager = new IntegrityManager();

    // Request the integrity token by providing a nonce.
    var tokenRequest = new IntegrityTokenRequest(nonce);
    var requestIntegrityTokenOperation =
        integrityManager.RequestIntegrityToken(tokenRequest);

    // Wait for PlayAsyncOperation to complete.
    yield return requestIntegrityTokenOperation;

    // Check the resulting error code.
    if (requestIntegrityTokenOperation.Error != IntegrityErrorCode.NoError)
    {
        AppendStatusLog("IntegrityAsyncOperation failed with error: " +
                requestIntegrityTokenOperation.Error);
        yield break;
    }

    // Get the response.
    var tokenResponse = requestIntegrityTokenOperation.GetResult();
}

Yerel

/// Create an IntegrityTokenRequest opaque object.
const char* nonce = RequestNonceFromServer();
IntegrityTokenRequest* request;
IntegrityTokenRequest_create(&request);
IntegrityTokenRequest_setNonce(request, nonce);

/// Prepare an IntegrityTokenResponse opaque type pointer and call
/// IntegerityManager_requestIntegrityToken().
IntegrityTokenResponse* response;
IntegrityErrorCode error_code =
        IntegrityManager_requestIntegrityToken(request, &response);

/// ...
/// Proceed to polling iff error_code == INTEGRITY_NO_ERROR
if (error_code != INTEGRITY_NO_ERROR)
{
    /// Remember to call the *_destroy() functions.
    return;
}
/// ...
/// Use polling to wait for the async operation to complete.
/// Note, the polling shouldn't block the thread where the IntegrityManager
/// is running.

IntegrityResponseStatus response_status;

/// Check for error codes.
IntegrityErrorCode error_code =
        IntegrityTokenResponse_getStatus(response, &response_status);
if (error_code == INTEGRITY_NO_ERROR
    && response_status == INTEGRITY_RESPONSE_COMPLETED)
{
    const char* integrity_token = IntegrityTokenResponse_getToken(response);
    SendTokenToServer(integrity_token);
}
/// ...
/// Remember to free up resources.
IntegrityTokenRequest_destroy(request);
IntegrityTokenResponse_destroy(response);
IntegrityManager_destroy();

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

Entegrasyon kararı istediğinizde Play Integrity API imzalı bir yanıt jetonu sağlar. İsteğinize eklediğiniz nonce, yanıt jetonunun bir parçası olur.

Jeton biçimi

Jeton, iç içe yerleştirilmiş bir JSON Web Token'dır (JWT), yani JSON Web İmzası'na (JWS) ait JSON Web Encryption (JWE). JWE ve JWS bileşenleri, kompakt serileştirme kullanılarak temsil edilir.

Şifreleme / imzalama algoritmaları, çeşitli JWT uygulamalarında iyi bir şekilde desteklenir:

  • JWE, alg için A256KW, enc için de A256GCM'yi kullanır

  • JWS, ES256 kullanır.

Google'ın sunucularında şifre çözme ve doğrulama (önerilir)

Play Integrity API, Google'ın sunucularında entegrasyon kararının şifresini çözmenizi ve doğrulamanızı sağlayarak uygulamanızın güvenliğini artırır. Bunun için şu adımları tamamlayı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.

Yerel olarak şifre çözme ve doğrulama

Yanıt şifreleme anahtarlarınızı yönetmeyi ve indirmeyi seçerseniz döndürülen jetonun şifresini kendi güvenli sunucu ortamınızda çözebilir ve doğrulayabilirsiniz. Döndürülen jetonu IntegrityTokenResponse#token() yöntemini kullanarak alabilirsiniz.

Aşağıdaki örnekte, Play Console'dan imza doğrulaması için AES anahtarının ve DER kodlu genel EC anahtarının, uygulamanın arka ucundaki dile özgü anahtarlara (bu örnekte Java programlama dili) nasıl çözüleceği gösterilmektedir. Anahtarların, varsayılan işaretler kullanılarak base64 olarak kodlandığını unutmayın.

Kotlin

// base64OfEncodedDecryptionKey is provided through Play Console.
var decryptionKeyBytes: ByteArray =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT)

// Deserialized encryption (symmetric) key.
var decryptionKey: SecretKey = SecretKeySpec(
    decryptionKeyBytes,
    /* offset= */ 0,
    AES_KEY_SIZE_BYTES,
    AES_KEY_TYPE
)

// base64OfEncodedVerificationKey is provided through Play Console.
var encodedVerificationKey: ByteArray =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT)

// Deserialized verification (public) key.
var verificationKey: PublicKey = KeyFactory.getInstance(EC_KEY_TYPE)
    .generatePublic(X509EncodedKeySpec(encodedVerificationKey))

Java


// base64OfEncodedDecryptionKey is provided through Play Console.
byte[] decryptionKeyBytes =
    Base64.decode(base64OfEncodedDecryptionKey, Base64.DEFAULT);

// Deserialized encryption (symmetric) key.
SecretKey decryptionKey =
    new SecretKeySpec(
        decryptionKeyBytes,
        /* offset= */ 0,
        AES_KEY_SIZE_BYTES,
        AES_KEY_TYPE);

// base64OfEncodedVerificationKey is provided through Play Console.
byte[] encodedVerificationKey =
    Base64.decode(base64OfEncodedVerificationKey, Base64.DEFAULT);
// Deserialized verification (public) key.
PublicKey verificationKey =
    KeyFactory.getInstance(EC_KEY_TYPE)
        .generatePublic(new X509EncodedKeySpec(encodedVerificationKey));

Daha sonra, ilk olarak bütünlük jetonunun (JWE kısmı) şifresini çözmek için bu anahtarları kullanın ve iç içe yerleştirilmiş JWS kısmını doğrulayıp çıkarın.

Kotlin

val jwe: JsonWebEncryption =
    JsonWebStructure.fromCompactSerialization(integrityToken) as JsonWebEncryption
jwe.setKey(decryptionKey)

// This also decrypts the JWE token.
val compactJws: String = jwe.getPayload()

val jws: JsonWebSignature =
    JsonWebStructure.fromCompactSerialization(compactJws) as JsonWebSignature
jws.setKey(verificationKey)

// This also verifies the signature.
val payload: String = jws.getPayload()

Java

JsonWebEncryption jwe =
    (JsonWebEncryption)JsonWebStructure
        .fromCompactSerialization(integrityToken);
jwe.setKey(decryptionKey);

// This also decrypts the JWE token.
String compactJws = jwe.getPayload();

JsonWebSignature jws =
    (JsonWebSignature) JsonWebStructure.fromCompactSerialization(compactJws);
jws.setKey(verificationKey);

// This also verifies the signature.
String payload = jws.getPayload();

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