Klasik API isteği yapma

Yalnızca geliştiricilerin çoğu için uygun olan standart API istekleri yapmayı planlıyorsanız bütünlük kararları bölümüne atlayabilirsiniz. Bu sayfada, Android 4.4 (API düzeyi 19) veya sonraki sürümlerde desteklenen, bütünlük kararları için klasik API isteklerinde bulunma hakkında bilgi verilmektedir.

Dikkat edilmesi gereken noktalar

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

Uygulamanızın güvenlik ve kötüye kullanım karşıtı ihtiyaçlarına bağlı olarak standart istekler, klasik istekler veya bu ikisinin bir kombinasyonunu gönderebilirsiniz. Standart istekler tüm uygulamalar ve oyunlar için uygundur ve herhangi bir işlemin veya sunucu çağrısının gerçek olup olmadığını kontrol etmek için kullanılabilir. Aynı zamanda tekrar oynanabilirlik ve veri hırsızlığına karşı bir miktar koruma için Google Play'e yetki verir. Klasik isteklerin oluşturulması daha pahalıdır ve hırsızlığa ve belirli saldırı türlerine karşı koruma sağlamak için bunları doğru şekilde uygulamak sizin sorumluluğunuzdadır. Klasik istekler, standart isteklerden daha seyrek gönderilmelidir. Örneğin, son derece değerli veya hassas bir işlemin gerçek olup olmadığını kontrol etmek için ara sıra bir kerelik bir istek yapılması gerekir.

Aşağıdaki tabloda, iki istek türü arasındaki temel farklılıklar 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 gereksinimleri Google Play Store ve Google Play Hizmetleri Google Play Store ve Google Play Hizmetleri
Entegrasyon ayrıntıları
API ısınması gerekiyor ✔️ (birkaç saniye)
Tipik istek gecikmesi Birkaç yüz milisaniye Birkaç saniye
Potansiyel istek sıklığı Sık (herhangi bir işlem veya istek için isteğe bağlı 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ısa ancak bir sunucu araması içerdiğinden uzun zaman aşımı önerilir (ör. 1 dakika). Karar istekleri istemci tarafında gerçekleşir İsteklerin çoğu 10 saniyeden kısa ancak bir sunucu çağrısı içerdiğinden uzun zaman aşımı önerilir (ör. 1 dakika)
Bütünlük kararı jetonu
Cihaz, uygulama ve hesap ayrıntılarını içerir ✔️ ✔️
Jetonu önbelleğe alma Google Play tarafından korumalı cihaz üzerinde önbelleğe alma Önerilmez
Jetonun şifresini Google Play sunucusu üzerinden çözün ve doğrulayın ✔️ ✔️
Tipik şifre çözme işlemi sunucudan sunucuya istek gecikmesi 10 saniyelik milisaniye (üç dokuzlu kullanılabilirlik) 10 saniyelik milisaniye (üç dokuzlu kullanılabilirlik)
Güvenli bir sunucu ortamında jetonların şifresini çözün ve yerel olarak doğrulayın ✔️
Jeton istemci taraflı şifre çözme ve doğrulama
Bütünlük kararı güncelliği Google Play tarafından yapılan bazı otomatik önbelleğe alma ve yenileme Her istekte tüm kararlar 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ı Hazırlamalar: Dakikada 5
Bütünlük jetonları: Herkese açık sınır yok*
Bütünlük jetonları: Dakikada 5
Koruma
Hileli değişikliklere ve benzer saldırılara karşı hafifletin requestHash alanını kullan İstek verilerine göre içerik bağlamayla nonce alanını kullan
Tekrar oynatmayı ve benzer saldırılara karşı hafifletin Google Play tarafından otomatik etki azaltma nonce alanını sunucu tarafı mantığıyla kullan

* Kamu sınırları dışındaki talepler de dahil olmak üzere tüm talepler, yüksek değerlerde kamu dışı savunma sınırlarına tabidir

Nadiren klasik istekler gönderme

Bütünlük jetonu oluşturmak zaman, veri ve pil kullanır. Ayrıca her uygulamanın günlük olarak gönderebileceği maksimum klasik istek sayısı vardır. Bu nedenle, yalnızca standart bir isteğe ek garanti istediğinizde en yüksek değere sahip veya en hassas işlemlerin gerçek olup olmadığını kontrol etmek için klasik isteklerde bulunmanız gerekir. Yüksek sıklık veya düşük değerli işlemler için klasik istek göndermemeniz gerekir. Uygulama her ön plana gittiğinde veya arka planda birkaç dakikada bir klasik istek göndermeyin ve aynı anda çok sayıda cihazdan çağrı yapmayın. Çok fazla klasik istek çağrısı yapan bir uygulama, kullanıcıları yanlış uygulamalara karşı korumak için kısıtlanabilir.

Kararları önbelleğe almaktan kaçınma

Bir sonucun önbelleğe alınması, güvenilmeyen bir ortamdan alınan iyi bir kararın yeniden kullanıldığı saldırı ve tekrar gibi saldırı riskini artırır. Klasik bir istek oluşturmayı ve daha sonra kullanmak üzere bunu önbelleğe almayı düşünüyorsanız, isteğe bağlı olarak standart bir istek gerçekleştirmeniz önerilir. Standart istekler cihazda bir miktar önbelleğe alma işlemi içerir ancak Google Play, tekrar oynatma saldırıları ve hırsızlık riskini azaltmak için ek koruma teknikleri kullanır.

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

Play Integrity API, nonce adlı bir alan sunar. Bu alan, uygulamanızı tekrar oynatma ve değişiklik saldırıları gibi belirli saldırılara karşı daha iyi korumak için kullanılabilir. 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 tek seferlik rastgele kod oluşturma ile ilgili kılavuzu dikkatli bir şekilde uygulayın.

Klasik istekleri eksponansiyel geri yüklemeyle yeniden deneme

Kararsız internet bağlantısı veya aşırı yüklü cihaz gibi ortam koşulları, cihaz bütünlüğü kontrollerinin başarısız olmasına neden olabilir. Bu durum, güvenilir olmayan bir cihaz için etiket oluşturulmasına yol açabilir. Bu senaryoları azaltmak için eksponansiyel geri yüklemeli bir yeniden deneme seçeneği ekleyin.

Genel bakış

Play Integrity API'nin üst düzey tasarımını gösteren adım sırası ş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ı tamamlayın:

  1. Uygulamanızın sunucu tarafı arka ucu, istemci tarafı mantığına benzersiz bir değer oluşturur ve gönderir. Geri kalan adımlarda bu mantığa "uygulamanız" denir.
  2. Uygulamanız, yüksek değere sahip işleminizin benzersiz değerinden ve içeriğinden nonce oluşturur. Ardından, nonce iletken 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 sonucu çö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.

Tek seferlik rastgele sayı oluştur

Uygulamanızdaki bir işlemi Play Integrity API ile koruduğunuzda, ortadaki kişi (PITM) müdahale ve tekrar oynama saldırıları gibi belirli saldırı türlerini hafifletmek için nonce alanından yararlanabilirsiniz. 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.

nonce alanında ayarlanan değer doğru biçimlendirilmiş olmalıdır:

  • String
  • URL güvenli
  • Base64 olarak kodlanır ve sarmalanmaz
  • En az 16 karakter
  • Maksimum 500 karakter

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

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

Bir isteğin içeriğini izinsiz değişikliklere karşı korumak için klasik bir API isteğindeki nonce parametresini, standart API isteğindeki requestHash parametresine benzer şekilde kullanabilirsiniz.

Entegrasyon kararı talep ettiğinizde:

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

Entegrasyon kararı aldığınızda:

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

Tekrar oynatma saldırılarına karşı korumak için benzersiz değerler ekleyin

Kötü amaçlı kullanıcıların Play Integrity API'den önceki yanıtları tekrar kullanmasını önlemek için her mesajı benzersiz şekilde tanımlamak amacıyla nonce alanını kullanabilirsiniz.

Entegrasyon kararı talep ettiğinizde:

  1. Kötü amaçlı kullanıcıların tahmin edemeyeceği şekilde genel olarak benzersiz bir değer elde etme. Ö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() öğesini ç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 edinin.
  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, cihazda benzersiz değer oluşturulmuşsa alınan değerin ilk kez kullanılıp kullanılmadığını kontrol edin (sunucunuzun uygun bir süre boyunca zaten görülmüş değerlerin kaydını tutması gerekir). Alınan benzersiz değer zaten kullanılmışsa isteği reddedin.

İzinsiz değişiklik yapma ve tekrar oynama saldırılarına karşı her iki korumayı birlikte kullanın (önerilir)

nonce alanını, aynı anda hem izinsiz hem de tekrar oynatma saldırılarına karşı koruma sağlamak için kullanılabilir. Bunu yapmak için yukarıda açıklandığı gibi benzersiz değeri oluşturun ve isteğinize ekleyin. Ardından, benzersiz değeri karmanın bir parçası olarak eklediğinizden emin olarak istek karmasını hesaplayın. Aşağıdaki iki yaklaşımı birleştiren bir uygulama:

Entegrasyon kararı talep ettiğinizde:

  1. Yüksek değerli işlemi kullanıcı başlatır.
  2. Tekrar oynatma saldırılarına karşı korumak için benzersiz değerler ekleme bölümünde açıklandığı şekilde bu işlem için benzersiz bir değer elde edin.
  3. Korumak istediğiniz bir iletiyi hazırlayın. 2. adımdaki benzersiz değeri iletiye ekleyin.
  4. Uygulamanız, korumak istediği mesajın özetini hesaplar. Bu işlem, Değişikliğe 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ımdan hesaplanan özete ayarlamak için setNonce() 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 özeti nonce alanından alın.
  3. Değişikliğe karşı koruma sağlamak için istek karması ekleme bölümünde açıklandığı gibi özeti sunucu tarafında yeniden hesaplayın ve bütünlük jetonundan alınan özetle eşleşip eşleşmediğini kontrol edin.
  4. Tekrar oynatma saldırılarına karşı korumak için benzersiz değerler ekleyin 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 hem de tekrar oynatma saldırılarına karşı nasıl korunacağınızı gösteren adım sırası diyagramı

Entegrasyon kararı isteyin

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. İlişkili derleyicideki setNonce() yöntemiyle nonce sağlayarak bir IntegrityTokenRequest oluşturun. Yalnızca Google Play dışında dağıtılan uygulamalar ve SDK'lar, Google Cloud proje numaralarını setCloudProjectNumber() yöntemi aracılığıyla belirtmek zorundadır. Google Play'deki uygulamalar, Play Console'daki bir Cloud projesine bağlıdır ve istekte Cloud proje numarasını belirtmeleri gerekmez.
  3. requestIntegrityToken() numarasını aramak için yöneticiyi kullanın ve IntegrityTokenRequest öğesini sağlayı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, JSON Web Signature'ın (JWS) JSON Web Encryption (JWE) protokolü olan, iç içe yerleştirilmiş bir JSON Web Token (JWT) türüdür. 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 ve enc için A256GCM'yi kullanır

  • JWS, ES256 kullanır.

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

Play Integrity API, Google'ın sunucularında entegrasyon kararının şifresini çözmenizi ve bu kararı doğrulamanıza olanak tanıyarak uygulamanızın güvenliğini artırır. Bunun için şu adımları uygulayın:

  1. Google Cloud projesinde uygulamanıza bağlı 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.

Şifresini çözün ve yerel olarak doğrulayın

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

Aşağıdaki örnekte, imza doğrulaması için Play Console'dan imza doğrulaması amacıyla AES anahtarının ve DER kodlamalı ortak EC anahtarının, uygulamanın arka ucundaki dile özgü (bizim durumumuzda Java programlama dili) anahtarlara 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));

Ardından, ilk olarak bütünlük jetonunun (JWE parçası) şifresini çözmek için bu anahtarları kullanın, ardından iç içe yerleştirilmiş JWS parçası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ı içeren düz metin biçiminde bir jetondur.