API de SafetyNet Attestation

La API de SafetyNet Attestation es una API antiabuso que les permite a los desarrolladores de apps evaluar el dispositivo Android en el que se ejecuta tu app. Debes usar esta API como parte de tu sistema de detección de abusos para determinar si tus servidores interactúan con tu app original, que se ejecuta en un dispositivo Android original.

La API de SafetyNet Attestation proporciona una certificación con firma criptográfica que evalúa la integridad del dispositivo. Para crear la certificación, la API examina el entorno de software y hardware del dispositivo, detecta problemas de integridad y los compara con los datos de referencia de los dispositivos Android aprobados. La certificación generada se vincula con el nonce que proporciona la app emisora. Además, incluye una marca de tiempo de generación y metadatos sobre la app solicitante.

La API no está diseñada para funcionar en los siguientes casos prácticos:

  • Como un mecanismo autónomo antiabuso o de seguridad para apps. Debes usarla junto con las prácticas recomendadas para la seguridad de las apps y tu conjunto de indicadores específicos de productos antiabuso.
  • Cuando el dispositivo no está conectado a Internet. En esos casos, la API muestra un error.
  • Hacer que su respuesta se interprete directamente en la app emisora. Mueve toda la lógica de decisión contra el abuso a un servidor que controles.
  • Proporcionar indicadores detallados sobre las modificaciones del sistema. La API ofrece valores booleanos que expresan diferentes niveles de integridad del sistema.
  • Incluir indicadores para casos prácticos específicos de apps, como identificadores de dispositivos, estado de emulación de GPS y estado de bloqueo de pantalla.
  • Reemplazar o implementar comprobaciones de DRM exhaustivas.
  • Solo verificar si el dispositivo cuenta con permisos de administrador, ya que la API está diseñada para verificar la integridad general del dispositivo.

Descripción general

La API de SafetyNet Attestation usa el siguiente flujo de trabajo:

  1. La API de SafetyNet Attestation recibe una llamada de tu app que incluye un nonce.
  2. El servicio de certificación de SafetyNet evalúa el entorno de tiempo de ejecución y solicita una certificación firmada de los resultados de la evaluación de los servidores de Google.
  3. Los servidores de Google envían la certificación firmada al servicio de certificación de SafetyNet del dispositivo.
  4. El servicio de certificación de SafetyNet muestra esta certificación firmada a tu app.
  5. Esta reenvía la certificación firmada a tu servidor.
  6. Este servidor valida la respuesta y la usa para tomar decisiones antiabuso. Además, comunica sus hallazgos a tu app.

En la figura 1, se muestra una representación gráfica de este proceso:

Figura 1: Protocolo de API de SafetyNet Attestation

Nota: Documentación adicional y lista de tareas

Durante la inicialización, la configuración y la activación de la API de SafetyNet Attestation, además de esta documentación principal, ten en cuenta y sigue estas recomendaciones:

Cómo obtener una clave de API

Si deseas invocar los métodos de la API de SafetyNet Attestation, debes usar una clave de API. Para crear una clave e incorporarla, completa estos pasos:

  1. Ve a la página Biblioteca de la Consola de API de Google.
  2. Busca y selecciona la API de Android Device Verification. Aparecerá la pantalla del panel de la API de Android Device Verification.
  3. Si no está habilitada, haz clic en Habilitar.
  4. Si aparece el botón Crear credenciales, haz clic en él para generar una clave de API. De lo contrario, haz clic en la lista desplegable Todas las credenciales de API y selecciona la clave de API asociada con el proyecto que habilitó la API de Android Device Verification.
  5. En la barra lateral de la izquierda, haz clic en Credenciales. Copia la clave de API que aparece.
  6. Úsala cuando llames al método attest() de la clase SafetyNetClient.

Después de crear esta clave de API, únete a la lista de distribución de clientes de la API de SafetyNet.

Cuota y supervisión de API

Importante: La asignación de cuota predeterminada (por proyecto) para llamar a la API de SafetyNet Attestation es de 10,000 solicitudes por día.

Si tu app requiere una cuota mayor, realiza una solicitud antes de implementarla para los usuarios. Para hacerlo, sigue estos pasos:

  1. Lee la documentación proporcionada para esta API. Es posible que se rechacen las solicitudes que no se ajusten a las prácticas recomendadas.
  2. Llena el formulario de solicitud de cuota.
  3. Recibirás un correo electrónico de confirmación que indica cuándo se procesó tu solicitud. Por lo general, se procesa dentro de los siguientes 2 a 3 días hábiles.

Nota: Independientemente de la cuota aprovisionada para tu proyecto, las instancias de apps individuales se limitan a un máximo de 5 solicitudes por minuto. Si se supera ese límite, todas las solicitudes restantes durante ese minuto mostrarán un error.

Ten en cuenta este comportamiento cuando implementes el mecanismo de reintento de tu app.

Sin importar cuál sea la cuota de la API de tu app, te recomendamos configurar la supervisión y las alertas de la cuota.

Cómo comprobar la versión de los Servicios de Google Play

Antes de utilizar la API de SafetyNet Attestation, debes asegurarte de que en el dispositivo del usuario esté instalada la versión correcta de los Servicios de Google Play. Si no es así, es posible que tu app deje de responder después de llamar a la API. Si tu app detecta que hay instalada una versión incorrecta, debes pedirle al usuario que actualice la app de los Servicios de Google Play en su dispositivo.

Para verificar si la versión instalada de los Servicios de Google Play es compatible con la versión del SDK de Android que estás usando, llama al método isGooglePlayServicesAvailable(), como se muestra en el siguiente fragmento de código:

if (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
            == ConnectionResult.SUCCESS) {
      // The SafetyNet Attestation API is available.
    } else {
      // Prompt user to update Google Play services.
    }
    

En los dispositivos que ejecutan los Servicios de Google Play v13.0 y versiones posteriores, la API de SafetyNet Attestation también admite claves de API restringidas por app. Esta función reduce el riesgo de uso accidental o no autorizado de claves de API con restricción de cuota. Para usar esta función opcional, comprueba que la versión de los Servicios de Google Play del dispositivo sea, al menos, v13.0, como se muestra en el siguiente fragmento de código:

if (GoogleApiAvailability.getInstance()
        .isGooglePlayServicesAvailable(context, 13000000) ==
        ConnectionResult.SUCCESS) {
      // The SafetyNet Attestation API is available.
    } else {
      // Prompt user to update Google Play Services.
    }
    

Cómo solicitar una certificación de SafetyNet

Después de obtener una clave de API válida para la API de Android Device Verification en la Consola de API de Google, tu app puede usar el servicio de certificación de SafetyNet. Para hacerlo, completa los siguientes pasos:

  1. Obtén un nonce.
  2. Solicita una certificación de SafetyNet.
  3. Transfiere la respuesta a tu servidor.
  4. Usa la respuesta de tu servidor, junto con sus otros indicadores antiabuso, para controlar el comportamiento de tu app.

Para que tu app siga siendo receptiva, ejecuta estos pasos fuera de su subproceso de ejecución principal. Para obtener más información sobre cómo crear subprocesos de ejecución separados, consulta Cómo enviar operaciones a varios subprocesos.

Debes realizar esta verificación para proteger todas las acciones críticas, incluidos los accesos, los eventos de compra y la adquisición de nuevos productos en la app. Sin embargo, las llamadas a la API de SafetyNet Attestation incurren en una mayor latencia y en un mayor uso de datos móviles y de la batería, por lo que es necesario encontrar un equilibrio entre seguridad y usabilidad. Como ejemplo, puedes optar por solicitar una certificación de SafetyNet durante el acceso y realizar verificaciones una vez cada 30 minutos, como máximo. También puedes permitir que tu servidor decida cuándo tu app debe solicitar una certificación, de modo que sea más difícil para los adversarios predecir el momento de la verificación.

Cómo obtener un nonce

Cuando llames a la API de SafetyNet Attestation, debes pasar un nonce. La certificación resultante contiene este nonce, lo que te permite determinar si la certificación pertenece a tu llamada a la API y no la reproduce un atacante.

Un nonce que se usa con una solicitud de SafetyNet debe tener al menos 16 bytes de longitud. Debes introducir variabilidad en tu nonce y asegurarte de que el mismo nonce nunca se use dos veces. Te recomendamos obtener parte del nonce de los datos que se envían a tus servidores. Por ejemplo, puedes concatenar el hash del nombre de usuario con la marca de tiempo de solicitud para formar el nonce.

Importante: Incluye tantos datos como sea posible en el nonce. Si lo haces, será más difícil para los atacantes realizar ataques de repetición. Por ejemplo, derivar el nonce del nombre de usuario limita los ataques de repetición a una misma cuenta. Sin embargo, derivar el nonce de todos los detalles de un evento de compra limita el uso del mensaje de respuesta de la API solo a ese evento de compra.

Cuando recibas la respuesta firmada de la API, compara siempre el nonce de la respuesta firmada con el que reconstruyes del resto del mensaje que se envió a tus servidores. Esta verificación garantiza que los atacantes no puedan reutilizar las certificaciones firmadas obtenidas de dispositivos confiables para otras solicitudes creadas con fines malintencionados.

Para obtener más información sobre el uso de las funciones de criptografía, consulta la guía sobre cómo usar criptografía.

Cómo solicitar la certificación

Una vez que hayas establecido una conexión con los Servicios de Google Play y creado un nonce, podrás realizar una solicitud de certificación de SafetyNet. Es posible que la respuesta a tu solicitud no sea inmediata, por lo que es mejor configurar un objeto de escucha de devolución de llamada para controlar la respuesta del servicio. En el siguiente fragmento de código, aparece un ejemplo de objeto de escucha:

Kotlin

    SafetyNet.getClient(this).attest(nonce, API_KEY)
        .addOnSuccessListener(this) {
            // Indicates communication with the service was successful.
            // Use response.getJwsResult() to get the result data.
        }
        .addOnFailureListener(this) { e ->
            // An error occurred while communicating with the service.
            if (e is ApiException) {
                // An error with the Google Play services API contains some
                // additional details.
                val apiException = e as ApiException

                // You can retrieve the status code using the
                // apiException.statusCode property.
            } else {
                // A different, unknown type of error occurred.
                Log.d(FragmentActivity.TAG, "Error: " + e.message)
            }
        }
    

Java

    // The nonce should be at least 16 bytes in length.
    // You must generate the value of API_KEY in the Google APIs dashboard.
    SafetyNet.getClient(this).attest(nonce, API_KEY)
        .addOnSuccessListener(this,
            new OnSuccessListener<SafetyNetApi.AttestationResponse>() {
                @Override
                public void onSuccess(SafetyNetApi.AttestationResponse response) {
                    // Indicates communication with the service was successful.
                    // Use response.getJwsResult() to get the result data.
                }
            })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // An error occurred while communicating with the service.
                if (e instanceof ApiException) {
                    // An error with the Google Play services API contains some
                    // additional details.
                    ApiException apiException = (ApiException) e;
                    // You can retrieve the status code using the
                    // apiException.getStatusCode() method.
                } else {
                    // A different, unknown type of error occurred.
                    Log.d(TAG, "Error: " + e.getMessage());
                }
            }
        });
    

El método onSuccess() indica que la comunicación con el servicio fue correcta, pero no indica si el dispositivo pasó la certificación de SafetyNet. En la siguiente sección, se explica cómo leer el resultado de la certificación y verificar su integridad.

Cómo transferir la respuesta de certificación de SafetyNet a tu servidor

Cuando tu app se comunica con SafetyNet, el servicio proporciona una respuesta que incluye el resultado de la certificación de SafetyNet e incluye información adicional para ayudarte a verificar la integridad del mensaje. El resultado se muestra como un objeto SafetyNetApi.AttestationResponse. Usa el método getJwsResult() de este objeto para obtener los datos de la solicitud. La respuesta tiene el formato JSON Web Signature (JWS).

Envía el objeto JWS a tu servidor para su validación y uso.

Cómo usar la respuesta de certificación de SafetyNet en tu servidor

En el siguiente extracto de JWS, se muestra el formato y el contenido de ejemplo de los datos de carga útil:

{
      "timestampMs": 9860437986543,
      "nonce": "R2Rra24fVm5xa2Mg",
      "apkPackageName": "com.package.name.of.requesting.app",
      "apkCertificateDigestSha256": ["base64 encoded, SHA-256 hash of the
                                      certificate used to sign requesting app"],
      "ctsProfileMatch": true,
      "basicIntegrity": true,
    }
    

La carga útil de una certificación firmada suele contener los siguientes campos:

Marca de tiempo de la respuesta

  • timestampMs: Milisegundos pasados de la época UNIX cuando los servidores de Google generaron el mensaje de respuesta de JWS.

Datos proporcionados por la app emisora

  • nonce: El token de un solo uso que la app emisora pasa a la API.

Datos sobre la app emisora

  • apkPackageName: Nombre del paquete de la app emisora.
  • apkCertificateDigestSha256: Representaciones codificadas en base 64 del hash SHA-256 de los certificados de firma de la app emisora.

Veredicto de integridad

  • ctsProfileMatch: Un veredicto más estricto sobre la integridad del dispositivo. Si el valor de ctsProfileMatch es true, el perfil del dispositivo que ejecuta tu app coincide con el perfil de un dispositivo que aprobó las pruebas de compatibilidad de Android.
  • basicIntegrity: Un veredicto menos estricto de la integridad del dispositivo. Si solo el valor de basicIntegrity es true, es probable que el dispositivo que ejecuta tu app no haya sido alterado. Sin embargo, no necesariamente el dispositivo aprobó las pruebas de compatibilidad de Android.

    Para obtener más información sobre las pruebas de compatibilidad de Android, consulta Diseño de un dispositivo Android y Compatibility Test Suite (CTS).

Campos opcionales

  • error: Información de error codificada relevante para la solicitud de API actual.
  • advice: Una sugerencia sobre cómo volver a poner un dispositivo en buen estado.

Veredictos de integridad potencial

El mensaje JWS contiene dos parámetros que indican el resultado de la verificación de la compatibilidad del dispositivo: ctsProfileMatch y basicIntegrity. El estado del dispositivo que ejecuta tu app podría afectar el valor de cada parámetro, como se muestra en la tabla 1:

Tabla 1: Ejemplos de cómo el estado del dispositivo podría afectar los valores de basicIntegrity y ctsProfileMatch

Estado del dispositivo Valor de ctsProfileMatch Valor de basicIntegrity
Dispositivo certificado y original que pasa CTS true true
Dispositivo certificado con bootloader desbloqueado false true
Dispositivo original pero no certificado (p. ej., cuando el fabricante no solicita certificación) false true
Dispositivo con ROM personalizada (sin permisos de administrador) false true
Emulador false false
Ningún dispositivo (p. ej., una secuencia de comandos de emulación de protocolo) false false
Señales de compromiso de integridad del sistema, una de las cuales puede ser el acceso a permisos de administrador false false
Señales de otros ataques activos, como trampas de API false false

Casos de error

El mensaje JWS también puede mostrar varios tipos de errores:

  • Un resultado null indica que no se completó la llamada al servicio de manera correcta.
  • Un parámetro de error en el JWS indica que ocurrió un problema, como un error de red o un error simulado por un atacante. La mayoría de los errores son transitorios y no se deberían producir si realizas otra llamada al servicio. Es posible que debas volver a intentarlo unas veces más con retrasos cada vez más grandes entre cada reintento.
  • Si se manipuló el dispositivo, es decir, si se establece basicIntegrity como falso en la respuesta, es posible que el veredicto no contenga datos sobre la app emisora, como apkPackageName y apkCertificateDigestSha256. Esto ocurre cuando nuestros sistemas no pueden determinar de manera confiable la app que realiza la llamada.

¿Qué se debe hacer cuando la certificación firmada informa un error?

  • Vuelve a intentarlo. Los errores en dispositivos legítimos son temporales y deberían desaparecer si se realiza otra llamada al servicio.
  • Verifica que tu app no llame a la API más de 5 veces por segundo en los dispositivos afectados y que aún no se haya agotado la cuota de API de tu proyecto.
  • Asume que podría ser un atacante que activa de manera intencional un caso de error para enmascarar su actividad.

Consejos para aprobar verificaciones futuras

Cuando está presente, el parámetro advice proporciona información para ayudar a explicar por qué la API de SafetyNet Attestation establece ctsProfileMatch o basicIntegrity como falsos en un resultado específico. El valor del parámetro incluye una lista de strings, como las del ejemplo siguiente:

{"advice": "LOCK_BOOTLOADER,RESTORE_TO_FACTORY_ROM"}
    

En tu app, puedes traducir los valores del parámetro advice en mensajes fáciles de usar para ayudar al usuario a aprobar futuras certificaciones de SafetyNet, como se muestra en la siguiente lista:

LOCK_BOOTLOADER
El usuario debería bloquear el bootloader de su dispositivo.
RESTORE_TO_FACTORY_ROM
El usuario debería restablecer su dispositivo con una ROM de fábrica.

Cómo verificar la respuesta de certificación de SafetyNet

Debes tomar medidas para asegurarte de que la respuesta de certificación de SafetyNet provenga del servicio de SafetyNet e incluya datos que coincidan con tu solicitud.

Para verificar el origen del mensaje JWS, completa los siguientes pasos:

  1. Extrae la cadena de certificados SSL del mensaje JWS.
  2. Valida la cadena de certificados SSL y usa la coincidencia de nombre de host SSL para verificar que el certificado de hoja se emitió al nombre de host attest.android.com.
  3. Usa el certificado para verificar la firma del mensaje JWS.
  4. Verifica los datos del mensaje JWS para asegurarte de que coincidan con los datos de tu solicitud original. En particular, asegúrate de que la marca de tiempo se haya validado y que el nonce, el nombre del paquete y los hashes de los certificados de firma de la app coincidan con los valores esperados.

Debes verificar la declaración JWS con soluciones criptográficas estándar, como las que se encuentran en el uso de la API de muestra de android-play-safetynet, disponible en GitHub.

Durante las pruebas y el desarrollo iniciales (pero no durante la producción), puedes llamar a una API en línea para verificar la firma de la declaración JWS. Este proceso también se mostró en el uso de la API de muestra de android-play-safetynet, que está disponible en GitHub. Ten en cuenta que la API de verificación en línea solo se usa para pruebas en etapas iniciales y tiene una cuota fija de 10,000 solicitudes por día.

Importante: El uso de la API de verificación en línea solo valida que el mensaje JWS esté firmado por los servidores de la API de SafetyNet Attestation. Esta API en línea no puede verificar si los campos de la carga útil coinciden con los valores que espera el servicio.

Cómo planificar casos inesperados

Te recomendamos planificar el uso de tu app para que tenga en cuenta los cambios y las interrupciones.

Cambios en la API
Pueden aparecer nuevos campos (experimentales) en el veredicto en cualquier momento. Asegúrate de que estos campos adicionales no dañen tu analizador ni la lógica de uso. En particular, no confíes en los campos experimentales antes de que se anuncien en la lista de distribución de clientes de la API de SafetyNet.
La API de SafetyNet Attestation no funciona

En el improbable caso de que la API de SafetyNet Attestation no esté disponible, se recomienda a los usuarios de esta API crear capacidades de servidor para controlar de manera dinámica la dependencia de la disponibilidad y de la calidad de esta API y su respuesta.

Las estrategias típicas deberían incluir la capacidad de instruir de manera dinámica a tus apps para que dejen de llamar a esta API, y contener listas blancas basadas en dispositivos y usuarios a fin de ignorar los resultados de la API de SafetyNet Attestation para ciertas clases de dispositivos y usuarios.

Código de ejemplo

Para obtener más orientación sobre cómo trabajar con las API de SafetyNet, consulta el código de ejemplo que está disponible en GitHub.

Lista de anuncios

Te recomendamos unirte a la lista de distribución de clientes de la API de SafetyNet para recibir actualizaciones sobre la API de SafetyNet Attestation.

Comentarios

Procura proporcionar tus comentarios sobre esta API. Los utilizaremos para dar prioridad a nuevas funciones y capacidades para esta API.

Más información

Para obtener más información sobre las prácticas recomendadas para usar la API de SafetyNet Attestation, consulta los siguientes vínculos:

Condiciones del Servicio adicionales

Cuando accedes a las API de SafetyNet o las usas, aceptas las Condiciones del Servicio de las API de Google y estas Condiciones adicionales. Asegúrate de leer y entender todos los términos y las políticas aplicables antes de acceder a las API.

Condiciones del Servicio de SafetyNet

Al igual que con cualquier dato recopilado en gran volumen durante la observación en el campo, existe la posibilidad de encontrar falsos positivos y falsos negativos. Presentaremos los datos según nuestros conocimientos. Probamos nuestros mecanismos de detección de manera exhaustiva a fin de garantizar la precisión y nos comprometemos a mejorar estos métodos con el tiempo para garantizar que continúen siendo precisos.

Acepta cumplir con todas las leyes, regulaciones y derechos aplicables de terceros (incluidas, entre otras, las leyes relativas a la importación o exportación de datos o software, y a la privacidad, y las leyes locales). No usará las API para alentar ni promover actividades ilegales o la violación de los derechos de terceros. No violará ninguna otra condición del servicio de Google (o sus afiliados).

Reconoce y comprende que la API de SafetyNet funciona mediante la recopilación de información de hardware y software, como datos de dispositivos y apps, y resultados de certificaciones de SafetyNet, y que envía esos datos a Google para su análisis. De acuerdo con el Artículo 3(d) de las Condiciones del Servicio de las API de Google, acepta que, si utiliza las API, es responsable de brindar avisos o el consentimiento necesario para la recopilación y el uso compartido de estos datos con Google.