Membuat permintaan API standar

Halaman ini menjelaskan pembuatan permintaan API standar untuk verdict integritas, yang didukung di Android 5.0 (API level 21) atau yang lebih tinggi. Anda dapat membuat permintaan API standar untuk verdict integritas setiap kali aplikasi Anda melakukan panggilan server untuk memeriksa apakah interaksi tersebut asli.

Ringkasan

Diagram urutan yang menunjukkan desain tingkat tinggi Play Integrity
API.

Permintaan standar terdiri dari dua bagian:

  • Menyiapkan penyedia token integritas (satu kali saja): Anda harus memanggil Integrity API untuk mempersiapkan penyedia token integritas jauh sebelum Anda perlu mendapatkan verdict integritas. Misalnya, Anda dapat melakukannya saat aplikasi diluncurkan atau di latar belakang sebelum verdict integritas diperlukan.
  • Meminta token integritas (on demand): Setiap kali aplikasi Anda membuat permintaan server yang ingin Anda periksa keasliannya, Anda meminta token integritas dan mengirimkannya ke server backend aplikasi Anda untuk dekripsi dan verifikasi. Kemudian, server backend Anda dapat menentukan tindakan yang diambil.

Menyiapkan penyedia token integritas (satu kali saja):

  1. Aplikasi Anda memanggil penyedia token integritas dengan nomor project Google Cloud Anda.
  2. Aplikasi Anda menyimpan penyedia token integritas di memori untuk panggilan pemeriksaan pengesahan lebih lanjut.

Meminta token integritas (on demand):

  1. Untuk tindakan pengguna yang perlu dilindungi, aplikasi Anda akan menghitung hash (menggunakan algoritma hash yang sesuai seperti SHA256) dari permintaan yang akan dibuat.
  2. Aplikasi Anda meminta token integritas, dengan meneruskan hash permintaan.
  3. Aplikasi Anda menerima token integritas yang ditandatangani dan dienkripsi dari Play Integrity API.
  4. Aplikasi Anda meneruskan token integritas ke backend aplikasi.
  5. Backend aplikasi akan mengirimkan token ke server Google Play. Server Google Play mendekripsi dan memverifikasi verdict tersebut, yang akan menampilkan hasilnya ke backend aplikasi Anda.
  6. Backend aplikasi akan memutuskan tindakan berikutnya, berdasarkan sinyal yang dimuat dalam payload token.
  7. Backend aplikasi akan mengirimkan hasil keputusan ke aplikasi.

Menyiapkan penyedia token integritas (satu kali saja)

Sebelum membuat permintaan standar untuk verdict integritas dari Google Play, Anda harus menyiapkan (atau "memanaskan") penyedia token integritas. Hal ini memungkinkan Google Play meng-cache informasi pengesahan parsial dengan cerdas di perangkat untuk mengurangi latensi di jalur penting saat Anda membuat permintaan untuk verdict integritas. Menyiapkan penyedia token lagi adalah cara untuk mengulangi pemeriksaan integritas dengan lebih sedikit resource, yang akan memperbarui verdict integritas yang Anda minta berikutnya.

Anda dapat menyiapkan penyedia token integritas:

  • Saat aplikasi diluncurkan (misalnya saat cold startup). Menyiapkan penyedia token bersifat asinkron, sehingga tidak akan memengaruhi waktu startup. Opsi ini akan berfungsi dengan baik jika Anda berencana untuk membuat permintaan verdict integritas segera setelah aplikasi diluncurkan, misalnya saat pengguna login atau pemain bergabung ke game.
  • Saat aplikasi Anda dibuka (misalnya saat warm startup). Namun, perlu diperhatikan bahwa setiap instance aplikasi hanya dapat menyiapkan token integritas hingga 5 kali per menit.
  • Kapan saja di latar belakang jika Anda ingin menyiapkan token sebelum permintaan verdict integritas.

Untuk menyiapkan penyedia token integritas, lakukan hal berikut:

  1. Buat StandardIntegrityManager seperti yang ditunjukkan dalam contoh berikut.
  2. Buat PrepareIntegrityTokenRequest, yang menyediakan Google Cloud nomor project melalui metode setCloudProjectNumber().
  3. Gunakan pengelola untuk memanggil prepareIntegrityToken() yang menyediakan 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();
}

Native

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

Melindungi permintaan dari modifikasi tidak sah (direkomendasikan)

Saat memeriksa tindakan pengguna di aplikasi Anda dengan Play Integrity API, Anda dapat memanfaatkan kolom requestHash untuk mengurangi serangan modifikasi yang tidak sah. Misalnya, game mungkin ingin melaporkan skor pemain ke server backend game, dan server Anda ingin memastikan skor ini tidak dimodifikasi secara tidak sah oleh server proxy. Play Integrity API menampilkan nilai yang Anda tetapkan dalam kolom requestHash, di dalam respons integritas yang ditandatangani. Tanpa requestHash, token integritas hanya akan terikat ke perangkat, tetapi tidak ke permintaan tertentu, yang membuka kemungkinan terjadinya serangan. Petunjuk berikut menjelaskan cara memanfaatkan kolom requestHash secara efektif:

Saat Anda meminta verdict integritas:

  • Hitung ringkasan semua parameter permintaan yang relevan (misalnya SHA256 dari serialisasi permintaan stabil) dari tindakan pengguna atau permintaan server yang terjadi. Nilai yang ditetapkan di kolom requestHash memiliki panjang maksimum 500 byte. Sertakan data permintaan aplikasi apa pun di requestHash yang penting atau relevan dengan tindakan yang Anda periksa atau lindungi. Kolom requestHash disertakan dalam verbatim token integritas, sehingga nilai yang panjang dapat meningkatkan ukuran permintaan.
  • Berikan ringkasan sebagai kolom requestHash ke Play Integrity API, dan dapatkan token integritas.

Saat Anda menerima verdict integritas:

  • Dekode token integritas, lalu ekstrak kolom requestHash.
  • Hitung ringkasan permintaan dengan cara yang sama seperti pada aplikasi (misalnya SHA256 dari serialisasi permintaan stabil).
  • Bandingkan ringkasan sisi aplikasi dan sisi server. Jika tidak cocok, permintaan tidak dapat dipercaya.

Meminta verdict integritas (on demand)

Setelah menyiapkan penyedia token integritas, Anda dapat mulai meminta verdict integritas dari Google Play. Caranya, selesaikan langkah-langkah berikut:

  1. Dapatkan StandardIntegrityTokenProvider, seperti ditunjukkan di atas.
  2. Buat StandardIntegrityTokenRequest, yang menyediakan hash permintaan tindakan pengguna yang ingin Anda lindungi melalui metode setRequestHash.
  3. Gunakan penyedia token integritas untuk memanggil request(), yang menyediakan 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();
}

Native

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

Mendekripsi dan memverifikasi verdict integritas

Setelah Anda meminta verdict integritas, Play Integrity API akan memberikan token respons yang dienkripsi. Untuk mendapatkan verdict integritas perangkat, Anda harus mendekripsi token integritas di server Google. Untuk melakukannya, selesaikan langkah-langkah berikut:

  1. Membuat akun layanan dalam project Google Cloud yang ditautkan ke aplikasi Anda.
  2. Di server aplikasi Anda, ambil token akses dari kredensial akun layanan menggunakan cakupan playintegrity, dan buat permintaan berikut:

    playintegrity.googleapis.com/v1/PACKAGE_NAME:decodeIntegrityToken -d \
    '{ "integrity_token": "INTEGRITY_TOKEN" }'
  3. Baca respons JSON.

Payload yang dihasilkan adalah token teks biasa yang berisi verdict integritas.

Perlindungan otomatis dari serangan replay

Untuk memitigasi serangan replay, Google Play otomatis memastikan setiap token integritas tidak dapat digunakan kembali berkali-kali. Mencoba mendekripsi token yang sama secara berulang kali akan menghasilkan verdict kosong.