Esta página descreve como interpretar e trabalhar com o veredito de integridade retornado. Se você fizer uma solicitação de API padrão ou clássica, o veredito de integridade será retornado no mesmo formato com conteúdo semelhante. O veredito de integridade comunica informações sobre a validade de dispositivos, apps e contas. O servidor do app pode usar o payload resultante em um veredito descriptografado e verificado para determinar a melhor forma de prosseguir com uma ação ou solicitação específica no app.
Formato de veredito de integridade retornado
O payload é JSON em texto simples e contém sinais de integridade e informações fornecidas pelo desenvolvedor.
A estrutura geral do payload é esta:
{ requestDetails: { ... } appIntegrity: { ... } deviceIntegrity: { ... } accountDetails: { ... } environmentDetails: { ... } }
Primeiro, confira se os valores no campo requestDetails
correspondem aos
da solicitação original antes de verificar cada veredito de integridade. As seções
abaixo descrevem cada campo em mais detalhes.
Campo de detalhes da solicitação
O campo requestDetails
contém informações sobre a solicitação, incluindo
informações fornecidas pelo desenvolvedor no requestHash
para solicitações padrão e
no nonce
para solicitações clássicas.
Para solicitações de API padrão:
requestDetails: { // Application package name this attestation was requested for. // Note that this field might be spoofed in the middle of the request. requestPackageName: "com.package.name" // Request hash provided by the developer. requestHash: "aGVsbG8gd29scmQgdGhlcmU" // The timestamp in milliseconds when the integrity token // was requested. timestampMillis: "1675655009345" }
Esses valores precisam corresponder aos da
solicitação original. Verifique a parte
requestDetails
do payload JSON, garantindo que o
requestPackageName
e o requestHash
correspondam ao que foi enviado na solicitação original, conforme
mostrado no snippet de código abaixo:
Kotlin
val requestDetails = JSONObject(payload).getJSONObject("requestDetails") val requestPackageName = requestDetails.getString("requestPackageName") val requestHash = requestDetails.getString("requestHash") val timestampMillis = requestDetails.getLong("timestampMillis") val currentTimestampMillis = ... // Ensure the token is from your app. if (!requestPackageName.equals(expectedPackageName) // Ensure the token is for this specific request || !requestHash.equals(expectedRequestHash) // Ensure the freshness of the token. || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) { // The token is invalid! See below for further checks. ... }
Java
RequestDetails requestDetails = decodeIntegrityTokenResponse .getTokenPayloadExternal() .getRequestDetails(); String requestPackageName = requestDetails.getRequestPackageName(); String requestHash = requestDetails.getRequestHash(); long timestampMillis = requestDetails.getTimestampMillis(); long currentTimestampMillis = ...; // Ensure the token is from your app. if (!requestPackageName.equals(expectedPackageName) // Ensure the token is for this specific request. || !requestHash.equals(expectedRequestHash) // Ensure the freshness of the token. || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) { // The token is invalid! See below for further checks. ... }
Para solicitações de API clássicas:
requestDetails: { // Application package name this attestation was requested for. // Note that this field might be spoofed in the middle of the // request. requestPackageName: "com.package.name" // base64-encoded URL-safe no-wrap nonce provided by the developer. nonce: "aGVsbG8gd29scmQgdGhlcmU" // The timestamp in milliseconds when the request was made // (computed on the server). timestampMillis: "1617893780" }
Esses valores precisam corresponder aos da
solicitação original. Portanto, verifique a parte
requestDetails
do payload JSON, garantindo que
requestPackageName
e nonce
correspondam ao que foi enviado na solicitação original, conforme
mostrado no snippet de código abaixo:
Kotlin
val requestDetails = JSONObject(payload).getJSONObject("requestDetails") val requestPackageName = requestDetails.getString("requestPackageName") val nonce = requestDetails.getString("nonce") val timestampMillis = requestDetails.getLong("timestampMillis") val currentTimestampMillis = ... // Ensure the token is from your app. if (!requestPackageName.equals(expectedPackageName) // Ensure the token is for this specific request. See 'Generate a nonce' // section of the doc on how to store/compute the expected nonce. || !nonce.equals(expectedNonce) // Ensure the freshness of the token. || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) { // The token is invalid! See below for further checks. ... }
Java
JSONObject requestDetails = new JSONObject(payload).getJSONObject("requestDetails"); String requestPackageName = requestDetails.getString("requestPackageName"); String nonce = requestDetails.getString("nonce"); long timestampMillis = requestDetails.getLong("timestampMillis"); long currentTimestampMillis = ...; // Ensure the token is from your app. if (!requestPackageName.equals(expectedPackageName) // Ensure the token is for this specific request. See 'Generate a nonce' // section of the doc on how to store/compute the expected nonce. || !nonce.equals(expectedNonce) // Ensure the freshness of the token. || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) { // The token is invalid! See below for further checks. ... }
Campo de integridade do aplicativo
O campo appIntegrity
contém informações relacionadas ao pacote.
appIntegrity: { // PLAY_RECOGNIZED, UNRECOGNIZED_VERSION, or UNEVALUATED. appRecognitionVerdict: "PLAY_RECOGNIZED" // The package name of the app. // This field is populated iff appRecognitionVerdict != UNEVALUATED. packageName: "com.package.name" // The sha256 digest of app certificates (base64-encoded URL-safe). // This field is populated iff appRecognitionVerdict != UNEVALUATED. certificateSha256Digest: ["6a6a1474b5cbbb2b1aa57e0bc3"] // The version of the app. // This field is populated iff appRecognitionVerdict != UNEVALUATED. versionCode: "42" }
appRecognitionVerdict
pode ter estes valores:
PLAY_RECOGNIZED
- O app e o certificado correspondem às versões distribuídas pelo Google Play.
UNRECOGNIZED_VERSION
- O nome do certificado ou do pacote não corresponde aos registros do Google Play.
UNEVALUATED
- A integridade do aplicativo não foi avaliada. Um requisito necessário está ausente, por exemplo, o dispositivo não é confiável o suficiente.
Para garantir que o token tenha sido gerado por um app criado por você, confira se a integridade do aplicativo está de acordo com o esperado, conforme mostrado no snippet de código abaixo:
Kotlin
val appIntegrity = JSONObject(payload).getJSONObject("appIntegrity") val appRecognitionVerdict = appIntegrity.getString("appRecognitionVerdict") if (appRecognitionVerdict == "PLAY_RECOGNIZED") { // Looks good! }
Java
JSONObject appIntegrity = new JSONObject(payload).getJSONObject("appIntegrity"); String appRecognitionVerdict = appIntegrity.getString("appRecognitionVerdict"); if (appRecognitionVerdict.equals("PLAY_RECOGNIZED")) { // Looks good! }
Também é possível conferir manualmente o nome do pacote, a versão e os certificados do app.
Campo de integridade do dispositivo
O campo deviceIntegrity
pode conter um único valor,
deviceRecognitionVerdict
, que tem um ou mais identificadores que representam a capacidade de um
dispositivo de garantir a integridade do app. Se um dispositivo não atender aos critérios de algum
identificador, o campo deviceIntegrity
vai ficar vazio.
deviceIntegrity: { // "MEETS_DEVICE_INTEGRITY" is one of several possible values. deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"] }
Por padrão, deviceRecognitionVerdict
pode conter o seguinte:
MEETS_DEVICE_INTEGRITY
- O app está em execução em um dispositivo Android com o Google Play Services. O dispositivo é aprovado nas verificações de integridade do sistema e atende aos requisitos de compatibilidade do Android.
- Vazio (um valor em branco)
- O app está sendo executado em um dispositivo que tem sinais de ataque, como hooks de API, ou comprometimentos do sistema, como acesso root. Ou, então, o app não está sendo executado em um dispositivo físico, mas sim, por exemplo, em um emulador que falha nas verificações de integridade do Google Play.
Para garantir que o token veio de um dispositivo confiável, verifique se o
deviceRecognitionVerdict
está de acordo com o esperado, conforme mostrado no snippet de
código abaixo:
Kotlin
val deviceIntegrity = JSONObject(payload).getJSONObject("deviceIntegrity") val deviceRecognitionVerdict = if (deviceIntegrity.has("deviceRecognitionVerdict")) { deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString() } else { "" } if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) { // Looks good! }
Java
JSONObject deviceIntegrity = new JSONObject(payload).getJSONObject("deviceIntegrity"); String deviceRecognitionVerdict = deviceIntegrity.has("deviceRecognitionVerdict") ? deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString() : ""; if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) { // Looks good! }
Se você tiver problemas em atender aos requisitos de integridade com o dispositivo de teste, confira se a ROM de fábrica está instalada, por exemplo, redefinindo o dispositivo para as configurações originais, e se o carregador de inicialização está bloqueado. Você também pode criar testes da Play Integrity no Play Console.
Identificadores de dispositivos condicionais
Se o app estiver sendo lançado no Google Play Games para PC, o
deviceRecognitionVerdict
também pode conter o seguinte identificador:
MEETS_VIRTUAL_INTEGRITY
- O app está em execução em um dispositivo Android com o Google Play Services. O emulador foi aprovado nas verificações de integridade do sistema e atende aos principais requisitos de compatibilidade do Android.
Informações opcionais do dispositivo
Se você aceitar receber outros
identificadores no veredito de integridade,
o campo deviceRecognitionVerdict
poderá ter os indicadores abaixo:
MEETS_BASIC_INTEGRITY
- O app está sendo executado em um dispositivo que é aprovado nas verificações básicas de integridade do sistema. O dispositivo pode não atender aos requisitos de compatibilidade do Android e não ser aprovado para executar o Google Play Services. Por exemplo, o dispositivo pode estar executando uma versão não reconhecida do Android, ter um carregador de inicialização desbloqueado ou não ter sido certificado pelo fabricante.
MEETS_STRONG_INTEGRITY
- O app está sendo executado em um dispositivo Android com o Google Play Services e tem uma forte garantia de integridade do sistema, como uma prova de integridade da inicialização protegida por hardware. O dispositivo é aprovado nas verificações de integridade do sistema e atende aos requisitos de compatibilidade do Android.
Um único dispositivo vai retornar vários identificadores de dispositivo no veredito de integridade se cada um dos critérios do identificador for atendido.
Atividade recente do dispositivo
Você também pode ativar a atividade recente do dispositivo, que informa quantas vezes seu app solicitou um token de integridade em um dispositivo específico na última hora. É possível usar a atividade recente do dispositivo para proteger seu app contra dispositivos hiperativos inesperados que podem indicar um ataque ativo. Também é possível decidir o quanto confiar em cada nível de atividade recente do dispositivo com base em quantas vezes você espera que o app instalado em um dispositivo comum solicite um token de integridade a cada hora.
Se você ativar o recebimento de recentDeviceActivity
, o campo deviceIntegrity
terá dois valores:
deviceIntegrity: { deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"] recentDeviceActivity: { // "LEVEL_2" is one of several possible values. deviceActivityLevel: "LEVEL_2" } }
As definições de deviceActivityLevel
diferem entre os modos e podem ter
um dos seguintes valores:
Nível de atividade recente do dispositivo | Solicitações padrão de token de integridade da API neste dispositivo na última hora por app | Solicitações clássicas de token de integridade da API neste dispositivo na última hora por app |
---|---|---|
LEVEL_1 (menor) |
10 ou menos | 5 ou menos |
LEVEL_2 |
Entre 11 e 25 | Entre 6 e 10 |
LEVEL_3 |
Entre 26 e 50 | Entre 11 e 15 |
LEVEL_4 (maior) |
Mais de 50 | Mais de 15 |
UNEVALUATED |
A atividade recente do dispositivo não foi avaliada. Isso pode acontecer
porque:
|
Campo de detalhes da conta
O campo accountDetails
contém um único valor, appLicensingVerdict
, que
representa o status de licenciamento do app no Google Play para a conta de usuário
conectado no dispositivo. Se a conta de usuário tiver a licença do Google Play para o app,
isso significa que eles fizeram o download
ou compraram pelo Google Play.
accountDetails: { // This field can be LICENSED, UNLICENSED, or UNEVALUATED. appLicensingVerdict: "LICENSED" }
O appLicensingVerdict
pode ter um destes valores:
LICENSED
- O usuário tem direito de acesso ao app. Em outras palavras, o usuário instalou ou comprou seu app no Google Play.
UNLICENSED
- O usuário não tem a titularidade do app. Isso acontece quando, por exemplo, o usuário transfere o app por sideload ou não o adquire pelo Google Play. Você pode mostrar a caixa de diálogo GET_LICENSED aos usuários para resolver isso.
UNEVALUATED
Os detalhes de licenciamento não foram avaliados porque um requisito necessário está ausente.
Isso pode acontecer por vários motivos, incluindo:
- O dispositivo não é confiável o suficiente.
- A versão do app instalada no dispositivo não é reconhecida pelo Google Play.
- O usuário não está conectado ao Google Play.
Para conferir se o usuário tem direito de acesso ao app, confira se o
appLicensingVerdict
está de acordo com o esperado, conforme mostrado no snippet de código abaixo.
Kotlin
val accountDetails = JSONObject(payload).getJSONObject("accountDetails") val appLicensingVerdict = accountDetails.getString("appLicensingVerdict") if (appLicensingVerdict == "LICENSED") { // Looks good! }
Java
JSONObject accountDetails = new JSONObject(payload).getJSONObject("accountDetails"); String appLicensingVerdict = accountDetails.getString("appLicensingVerdict"); if (appLicensingVerdict.equals("LICENSED")) { // Looks good! }
Campo de detalhes do ambiente
Você também pode ativar outros indicadores sobre o ambiente. Acesso de apps informa ao seu aplicativo se houver outros aplicativos em execução que possam ser usados para capturam a tela, mostram sobreposições ou controlam o dispositivo. Play Protect veredito informa se o Google Play Protect está ativado no dispositivo e se ele encontrou um malware conhecido.
Se você tiver ativado o veredito do indicador de risco de acesso a apps ou do Play Protect
no Google Play Console, sua resposta da API incluirá os
environmentDetails
. O campo environmentDetails
pode conter duas
valores, appAccessRiskVerdict
e playProtectVerdict
.
Veredito de risco de acesso a apps (Beta)
Depois da ativação, o campo environmentDetails
no payload da API
Play Integrity vai conter
o novo veredito do indicador de risco de acesso a apps.
{
requestDetails: { ... }
appIntegrity: { ... }
deviceIntegrity: { ... }
accountDetails: { ... }
environmentDetails: {
appAccessRiskVerdict: {
// This field contains one or more responses, for example the following.
appsDetected: ["KNOWN_INSTALLED", "UNKNOWN_INSTALLED", "UNKNOWN_CAPTURING"]
}
}
}
Se o indicador de risco de acesso a apps tiver sido avaliado, appAccessRiskVerdict
conterá o campo
appsDetected
com uma ou mais respostas. Essas respostas se enquadram em um dos
dois grupos a seguir, dependendo da origem de instalação dos apps detectados:
Apps do sistema ou do Google Play: apps instalados pelo Google Play ou pré-carregados pelo fabricante na partição do sistema do dispositivo (identificada com
FLAG_SYSTEM
). As respostas para esses apps têm o prefixoKNOWN_
.Outros apps: são apps que não são instalados pelo Google Play. Isso exclui apps pré-carregados na partição do sistema pelo fabricante do dispositivo. Respostas nesses apps tem o prefixo
UNKNOWN_
.
As seguintes respostas podem ser retornadas:
KNOWN_INSTALLED
,UNKNOWN_INSTALLED
- Há apps instalados que correspondem à origem de instalação correspondente.
KNOWN_CAPTURING
,UNKNOWN_CAPTURING
- Há apps em execução com permissões ativadas que podem ser usadas para visualizar a tela enquanto o app está em execução. Isso exclui qualquer de acessibilidade conhecidos pelo Google Play em execução no dispositivo.
KNOWN_CONTROLLING
,UNKNOWN_CONTROLLING
- Há apps em execução com permissões ativadas que podem ser usadas para controlar o dispositivo e controlar diretamente as entradas no seu app e podem ser usada para capturar entradas e saídas do seu app. Isso exclui qualquer de acessibilidade conhecidos pelo Google Play em execução no dispositivo.
KNOWN_OVERLAYS
,UNKNOWN_OVERLAYS
- Há apps em execução com permissões ativadas que podem ser usadas para exibir sobreposições em seu app. Isso exclui qualquer acessibilidade verificada serviços conhecidos pelo Google Play em execução no dispositivo.
- EMPTY (um valor em branco)
Ele não será avaliado se um requisito necessário estiver ausente. Em Neste caso, o campo
appAccessRiskVerdict
está vazio. Isso pode acontecer para vários motivos, incluindo os seguintes:- O dispositivo não é confiável o suficiente.
- O formato do dispositivo não é um smartphone, tablet ou dobrável.
- O dispositivo não está executando o Android 6 (nível 23 da API) ou mais recente.
- A versão do app instalada no dispositivo é desconhecida para o Google Play.
- A versão da Google Play Store no dispositivo está desatualizada.
- Somente para jogos: a conta de usuário não tem uma licença do Google Play para o jogo.
- Uma solicitação padrão foi usada com o parâmetro
verdictOptOut
. - Uma solicitação padrão foi usada com uma versão da biblioteca da API Play Integrity que ainda não oferece suporte ao indicador de risco de acesso a apps para solicitações padrão.
Ele exclui automaticamente os serviços de acessibilidade verificados que
passaram por uma revisão de acessibilidade aprimorada do Google Play (instalada pelo
em qualquer app store do dispositivo). "Excluído" significa que a acessibilidade verificada
serviços em execução no dispositivo não retornarão um estado de captura,
sobrepõe a resposta ao veredito de risco de acesso a apps. Para solicitar uma versão aprimorada
Reproduzir análise de acessibilidade para seu app de acessibilidade e publicá-lo no Google
Confira se o app tem a flag isAccessibilityTool
definida como verdadeira
manifesto do app ou peça uma revisão.
A tabela a seguir dá alguns exemplos de vereditos e o que eles significam não lista todos os resultados possíveis):
Exemplo de resposta para o veredito de risco de acesso a apps | Interpretação |
---|---|
appsDetected: ["KNOWN_INSTALLED"]
|
Somente os apps instalados são reconhecidos pelo Google Play ou foram pré-carregados na partição do sistema pelo fabricante do dispositivo. Não há apps em execução que resultariam nos vereditos de captura, controle ou sobreposição. |
appsDetected: ["KNOWN_INSTALLED", "UNKNOWN_INSTALLED", "UNKNOWN_CAPTURING"]
|
Há apps instalados pelo Google Play ou pré-carregados na partição do sistema pelo fabricante do dispositivo. Há outros apps em execução e com permissões ativadas que podem ser usadas para visualizar a tela ou capturar outras entradas e saídas. |
appsDetected: ["KNOWN_INSTALLED", "KNOWN_CAPTURING", "UNKNOWN_INSTALLED", "UNKNOWN_CONTROLLING"]
|
Há execução do Google Play ou do sistema com permissões ativadas que podem ser usadas para visualizar a tela ou capturar outras entradas e saídas. Há também outros apps em execução com permissões ativadas que podem ser usadas para controlar o dispositivo e controlar diretamente as entradas no seu app. |
appAccessRiskVerdict: {}
|
O indicador de risco de acesso a apps não foi avaliado porque um requisito necessário está ausente. Por exemplo, o dispositivo não era confiável o suficiente. |
Dependendo do seu nível de risco, é possível decidir qual combinação de vereditos é aceitável para prosseguir e para quais vereditos você quer tomar medidas. O snippet de código a seguir ilustra um exemplo de verificação de que não há Apps em execução que podem capturar a tela ou controlar seu app:
Kotlin
val environmentDetails = JSONObject(payload).getJSONObject("environmentDetails") val appAccessRiskVerdict = environmentDetails.getJSONObject("appAccessRiskVerdict") if (appAccessRiskVerdict.has("appsDetected")) { val appsDetected = appAccessRiskVerdict.getJSONArray("appsDetected").toString() if (!appsDetected.contains("CAPTURING") && !appsDetected.contains("CONTROLLING")) { // Looks good! } }
Java
JSONObject environmentDetails = new JSONObject(payload).getJSONObject("environmentDetails"); JSONObject appAccessRiskVerdict = environmentDetails.getJSONObject("appAccessRiskVerdict"); if (appAccessRiskVerdict.has("appsDetected")) { String appsDetected = appAccessRiskVerdict.getJSONArray("appsDetected").toString() if (!appsDetected.contains("CAPTURING") && !appsDetected.contains("CONTROLLING")) { // Looks good! } }
Corrigir vereditos de risco de acesso a apps
Dependendo do seu nível de risco, é possível decidir quais vereditos de risco de acesso a apps você devem tomar providências antes de permitir que o usuário conclua uma solicitação ou ação. Há solicitações opcionais do Google Play que você pode mostrar ao usuário depois verificar o veredito de risco de acesso a apps. Você pode mostrar CLOSE_UNKNOWN_ACCESS_RISK para pedir que o usuário feche apps desconhecidos, fazendo com que a veredito de risco de acesso a apps ou mostrar CLOSE_ALL_ACCESS_RISK para pedir o usuário feche todos os apps (conhecidos e desconhecidos) que causa o veredito de risco de acesso a apps.
Veredito do Play Protect
Depois de ativado, o campo environmentDetails
na API Play Integrity
payload vai conter
veredito do Play Protect:
environmentDetails: {
playProtectVerdict: "NO_ISSUES"
}
O playProtectVerdict
pode ter um destes valores:
NO_ISSUES
- O Play Protect está ativado e não encontrou problemas de apps no dispositivo.
NO_DATA
- O Play Protect está ativado, mas nenhuma verificação foi realizada ainda. O dispositivo ou o app Play Store pode ter sido redefinido recentemente.
POSSIBLE_RISK
- O Play Protect está desativado.
MEDIUM_RISK
- O Play Protect está ativado e encontrou apps potencialmente nocivos instalados no dispositivo.
HIGH_RISK
- O Play Protect está ativado e encontrou apps perigosos instalados no dispositivo.
UNEVALUATED
O veredito do Play Protect não foi avaliado.
Isso pode acontecer por vários motivos, incluindo:
- O dispositivo não é confiável o suficiente.
- Somente para jogos: a conta de usuário não tem uma licença do Google Play para o jogo.
Orientação sobre como usar o veredito do Play Protect
O servidor de back-end do app pode decidir o que fazer com base no veredito considerando sua tolerância a riscos. Aqui estão algumas sugestões e possíveis ações do usuário:
NO_ISSUES
- O Play Protect está ativado e não encontrou problemas. Por isso, nenhuma ação do usuário é necessária.
POSSIBLE_RISK
eNO_DATA
- Ao receber esses vereditos, peça ao usuário para conferir se o Play Protect está ativado
e se realizou uma verificação.
NO_DATA
pode aparecer apenas em raras circunstâncias. MEDIUM_RISK
eHIGH_RISK
- Dependendo da sua tolerância a riscos, você pode pedir que o usuário abra o Play Protect e tome medidas em relação aos avisos mostrados. Se o usuário não puder fazer o preenchimento esses requisitos, será possível bloqueá-los da ação do servidor.