Esegui una richiesta API standard

Questa pagina descrive come effettuare richieste API standard per gli esiti dell'integrità, che sono supportati su Android 5.0 (livello API 21) o versioni successive. Puoi effettuare una richiesta API standard per un esito di integrità ogni volta che la tua app effettua una chiamata al server per verificare se l'interazione è autentica.

Panoramica

Figura 1. Diagramma di sequenza che mostra la progettazione di alto livello dell'API Play Integrity.

Una richiesta standard è costituita da due parti:

  • Prepara il fornitore di token di integrità (una sola volta): devi chiamare l'API Integrity per preparare il fornitore di token di integrità molto prima di dover ottenere l'esito relativo all'integrità. Ad esempio, puoi farlo all'avvio dell'app o in background prima che sia necessario il verdetto di integrità.
  • Richiedi un token di integrità (on demand): ogni volta che la tua app effettua una richiesta al server che vuoi verificare sia autentica, richiedi un token di integrità e invialo al server di backend dell'app per la decriptografia e la verifica. Il server di backend può quindi determinare come agire.

Prepara il fornitore di token di integrità (una sola volta):

  1. La tua app chiama il fornitore di token di integrità con il numero del tuo progetto Google Cloud.
  2. La tua app mantiene in memoria il fornitore di token di integrità per ulteriori chiamate di controllo dell'attestazione.

Richiedi un token di integrità (on demand):

  1. Per l'azione dell'utente da proteggere, la tua app calcola l'hash (utilizzando un algoritmo hash adatto come SHA256) della richiesta da effettuare.
  2. La tua app richiede un token di integrità, passando l'hash della richiesta.
  3. La tua app riceve il token di integrità firmato e criptato dall'API Play Integrity.
  4. La tua app trasmette il token di integrità al backend dell'app.
  5. Il backend dell'app invia il token a un server di Google Play. Il server di Google Play decripta e verifica l'esito, restituendo i risultati al backend della tua app.
  6. Il backend della tua app determina come procedere in base agli indicatori contenuti nel payload del token.
  7. Il backend della tua app invia i risultati della decisione alla tua app.

Prepara il fornitore di token di integrità (una tantum)

Prima di effettuare una richiesta standard per un verdetto di integrità da Google Play, devi preparare (o "riscaldare") il fornitore di token di integrità. In questo modo, Google Play memorizza in modo intelligente nella cache le informazioni di attestazione parziale sul dispositivo per ridurre la latenza nel percorso critico quando fai una richiesta di verdetto di integrità. La preparazione di nuovo del fornitore di token è un modo per ripetere controlli di integrità meno pesanti per le risorse, il che renderà più aggiornato il successivo verdetto di integrità che richiedi.

Potresti preparare il fornitore di token di integrità:

  • All'avvio dell'app (ovvero all'avvio completo). La preparazione del fornitore di token è asincrona e quindi non influirà sul tempo di avvio. Questa opzione è adatta se prevedi di effettuare una richiesta di esito relativo all'integrità poco dopo l'avvio dell'app, ad esempio quando un utente accede o un giocatore partecipa a una partita.
  • Quando l'app viene aperta (ad es. all'avvio caldo). Tuttavia, tieni presente che ogni istanza dell'app può preparare il token di integrità fino a 5 volte al minuto.
  • In qualsiasi momento in background quando vuoi preparare il token in anticipo rispetto a una richiesta di esito dell'integrità.

Per preparare il fornitore di token di integrità:

  1. Crea un StandardIntegrityManager, come mostrato negli esempi seguenti.
  2. Costruisci un PrepareIntegrityTokenRequest, fornendo il numero di progetto Google Cloud tramite il metodo setCloudProjectNumber().
  3. Utilizza il gestore per chiamare prepareIntegrityToken(), fornendo 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();
}

Unreal Engine

// .h
void MyClass::OnPrepareIntegrityTokenCompleted(
  EStandardIntegrityErrorCode ErrorCode,
  UStandardIntegrityTokenProvider* Provider)
{
  // Check the resulting error code.
  if (ErrorCode == EStandardIntegrityErrorCode::StandardIntegrity_NO_ERROR)
  {
    // ...
  }
}

// .cpp
void MyClass::PrepareIntegrityToken()
{
  int64 CloudProjectNumber = ...

  // Create the Integrity Token Request.
  FPrepareIntegrityTokenRequest Request = { CloudProjectNumber };

  // Create a delegate to bind the callback function.
  FPrepareIntegrityOperationCompletedDelegate Delegate;

  // Bind the completion handler (OnPrepareIntegrityTokenCompleted) to the delegate.
  Delegate.BindDynamic(this, &MyClass::OnPrepareIntegrityTokenCompleted);

  // Initiate the prepare integrity token operation, passing the delegate to handle the result.
  GetGameInstance()
    ->GetSubsystem<UStandardIntegrityManager>()
    ->PrepareIntegrityToken(Request, Delegate);
}

Nativo

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

Proteggere le richieste da manomissioni (opzione consigliata)

Quando controlli un'azione dell'utente nella tua app con l'API Play Integrity, puoi utilizzare il campo requestHash per ridurre il rischio di attacchi di manomissione. Ad esempio, un gioco potrebbe voler comunicare il punteggio del giocatore al server di backend del gioco e il tuo server vuole assicurarsi che questo punteggio non sia stato manomesso da un server proxy. L'API Play Integrity restituisce il valore impostato nel campo requestHash all'interno della risposta di integrità firmata. Senza il requestHash, il token di integrità verrà associato solo al dispositivo, ma non alla richiesta specifica, il che apre la possibilità di attacco. Le seguenti istruzioni descrivono come utilizzare in modo efficace il campo requestHash:

Quando richiedi un esito relativo all'integrità:

  • Calcola un digest di tutti i parametri di richiesta pertinenti (ad es. SHA256 di una serializzazione stabile della richiesta) dall'azione dell'utente o dalla richiesta del server in corso. Il valore impostato nel campo requestHash ha una lunghezza massima di 500 byte. Includi tutti i dati delle richieste delle app nel requestHash che sono fondamentali o pertinenti all'azione che stai controllando o proteggendo. Il campo requestHash è incluso nel token di integrità letteralmente, quindi valori lunghi possono aumentare le dimensioni della richiesta.
  • Fornisci il digest come campo requestHash all'API Play Integrity e ottieni il token di integrità.

Quando ricevi un esito relativo all'integrità:

  • Decodifica il token di integrità ed estrai il campo requestHash.
  • Calcola un digest della richiesta nello stesso modo dell'app (ad es. SHA256 di una serializzazione stabile della richiesta).
  • Confronta i digest lato app e lato server. Se non corrispondono, la richiesta non è attendibile.

Richiedere un esito relativo all'integrità (on demand)

Dopo aver preparato il fornitore di token di integrità, puoi iniziare a richiedere gli esiti dell'integrità da Google Play. Per farlo, segui questi passaggi:

  1. Ottenere un StandardIntegrityTokenProvider.
  2. Crea un StandardIntegrityTokenRequest fornendo l'hash della richiesta dell'azione utente che vuoi proteggere tramite il metodo setRequestHash.
  3. Utilizza il fornitore di token di integrità per chiamare request(), fornendo 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();
}

Unreal Engine

// .h
void MyClass::OnRequestIntegrityTokenCompleted(
  EStandardIntegrityErrorCode ErrorCode,
  UStandardIntegrityToken* Response)
{
  // Check the resulting error code.
  if (ErrorCode == EStandardIntegrityErrorCode::StandardIntegrity_NO_ERROR)
  {
    // Get the token.
    FString Token = Response->Token;
  }
}

// .cpp
void MyClass::RequestIntegrityToken()
{
  UStandardIntegrityTokenProvider* Provider = ...

  // Prepare the UStandardIntegrityTokenProvider.

  // Request integrity token by providing a user action request hash. Can be called
  // several times for different user actions.
  FString RequestHash = ...;
  FStandardIntegrityTokenRequest Request = { RequestHash };

  // Create a delegate to bind the callback function.
  FStandardIntegrityOperationCompletedDelegate Delegate;

  // Bind the completion handler (OnRequestIntegrityTokenCompleted) to the delegate.
  Delegate.BindDynamic(this, &MyClass::OnRequestIntegrityTokenCompleted);

  // Initiate the standard integrity token request, passing the delegate to handle the result.
  Provider->Request(Request, Delegate);
}

Nativo

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

Se la tua app utilizza lo stesso fornitore di token per troppo tempo, il fornitore di token può scadere, il che comporta l'errore INTEGRITY_TOKEN_PROVIDER_INVALID nella richiesta di token successiva. Devi gestire questo errore richiedendo un nuovo fornitore.

Decriptare e verificare l'esito relativo all'integrità

Dopo aver richiesto un esito relativo all'integrità, l'API Play Integrity fornisce un token di risposta criptato. Per ottenere gli esiti dell'integrità del dispositivo, devi decriptare il token di integrità sui server di Google. Per farlo, segui questi passaggi:

  1. Crea un service account nel progetto Google Cloud collegato alla tua app.
  2. Sul server della tua app, recupera il token di accesso dalle credenziali del service account utilizzando l'ambito playintegrity ed esegui la seguente richiesta:

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

Il payload risultante è un token di testo normale che contiene verifiche dell'integrità.

Protezione automatica contro gli attacchi di replay

Per ridurre gli attacchi di replay, Google Play impedisce automaticamente il riutilizzo dei token di integrità molte volte. Se tenti di decriptare ripetutamente lo stesso token, i verdetti verranno cancellati come segue:

  • Il risultato del riconoscimento del dispositivo sarà vuoto.
  • L'esito del riconoscimento app e l'esito delle licenze dell'app verranno impostati su UNEVALUATED.
  • Tutti i verdetti facoltativi abilitati tramite Play Console verranno impostati su UNEVALUATED (o su un verdetto vuoto se si tratta di un verdetto con più valori).

Risolvere i problemi relativi al verdetto con un prompt di Google Play (facoltativo)

Dopo aver ricevuto un esito di integrità, il server può determinare come procedere. Se il verdetto indica che c'è un problema, ad esempio l'app non è concessa in licenza, è stata manomessa o il dispositivo è compromesso, puoi dare agli utenti la possibilità di risolvere il problema autonomamente.

L'API Play Integrity offre un'opzione per mostrare una finestra di dialogo di Google Play che chiede all'utente di intervenire, ad esempio per scaricare la versione ufficiale della tua app da Google Play.

Per scoprire come attivare queste finestre di dialogo dalla tua app in base alla risposta del server, consulta Finestre di dialogo di correzione.