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 donde se ejecuta su app. Se debe 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 devuelve un error.
  • Hacer que la respuesta se interprete directamente en la app que realiza la llamada. Mueve toda la lógica de decisión antiabuso 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 la app. La llamada 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 a 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 la 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. La API de SafetyNet Attestation dejó de estar disponible, por lo que ya no puedes solicitar una clave de API nueva.

Cuota y supervisión de API

La asignación de cuota predeterminada por proyecto para llamar a la API de SafetyNet Attestation es de 10,000 solicitudes por día en tu base de usuarios. Para realizar un mayor volumen de solicitudes de integridad, migra a la API de Play Integrity y solicita un aumento de cuota según las instrucciones de la documentación de la API de Play Integrity. Ten en cuenta que las solicitudes de cuota tardan algunos días hábiles en procesarse, por lo que se recomienda configurar la supervisión y las alertas de cuota para evitar situaciones de emergencia.

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.

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

Antes de utilizar la API de SafetyNet Attestation, debes asegurarte de que esté instalada la versión correcta de los Servicios de Google Play en el dispositivo del usuario. Si hay una versión incorrecta instalada, es posible que la app deje de responder después de llamar a la API. Si la app detecta que hay una versión incorrecta, debes pedirle al usuario que actualice la app de Servicios de Google Play en el 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 APIs de Google, tu app puede usar el servicio de certificación de SafetyNet. Para ello, 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 del servidor, junto con los otros indicadores antiabuso, para controlar el comportamiento de la app.

Para que tu app siga siendo receptiva, realiza estos pasos fuera del 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 productos integrados en la aplicación nuevos. Sin embargo, las llamadas a la API de SafetyNet Attestation tienen más latencia y 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 ejecutar 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

Cuando hayas establecido una conexión con los Servicios de Google Play y hayas creado un nonce, tendrás todo listo para hacer una solicitud de certificación de SafetyNet. Es posible que la respuesta a la solicitud no sea inmediata, por lo que es mejor configurar un objeto de escucha de devolución de llamada para manejar 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 se realizó correctamente la comunicación con el servicio, pero no indica si el dispositivo aprobó 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, además de información adicional para ayudarte a verificar la integridad del mensaje. El resultado se entrega como un objeto SafetyNetApi.AttestationResponse. Usa el método getJwsResult() del objeto para obtener los datos de la solicitud. La respuesta tiene el formato de una firma web JSON (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,
  "evaluationType": "BASIC",
  "deprecationInformation": "..."
}

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

Marca de tiempo de la respuesta

  • timestampMs: indica los milisegundos que transcurrieron desde la época UNIX cuando los servidores de Google generaron el mensaje de respuesta de JWS.

Datos proporcionados por la app que hace la llamada

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

Datos sobre la app que hace la llamada

  • apkPackageName: es el nombre del paquete de la app que hace la llamada.
  • apkCertificateDigestSha256: son representaciones codificadas en base 64 del hash SHA-256 de los certificados de firma de la app que hace la llamada

Veredicto de integridad

  • ctsProfileMatch: es 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 pasó las pruebas de compatibilidad de Android y se aprobó como dispositivo Android certificado por Google.
  • basicIntegrity: es un veredicto menos estricto de la integridad del dispositivo. Si solo el valor de basicIntegrity es true, es probable que no se haya alterado el dispositivo que ejecuta la app. Sin embargo, el dispositivo no necesariamente 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 Conjunto de pruebas de compatibilidad (CTS).

Información sobre la baja

  • deprecationInformation: Es una cadena que contiene información para desarrolladores sobre la baja de la API de SafetyNet Attestation.

Campos opcionales

  • error: es la información de error codificada relevante para la solicitud de API actual.
  • advice: es una sugerencia sobre cómo regresar un dispositivo a un estado bueno.
  • evaluationType: tipos de mediciones que contribuyeron a la respuesta actual de la API.

Veredictos de integridad posibles

El mensaje JWS tiene dos parámetros que indican el resultado de la verificación de 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 el 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 hubo 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 reintentarlo 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 tenga datos sobre la app que hace la llamada, 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 minuto 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 pasar 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 del dispositivo.
RESTORE_TO_FACTORY_ROM
El usuario debería restablecer el dispositivo con una ROM de fábrica.

Tipos de evaluación

El parámetro evaluationType está presente siempre que estén presentes los veredictos de ctsProfileMatch y basicIntegrity.

El parámetro proporciona información sobre los tipos de medidas que se usan para calcular campos como ctsProfileMatch y basicIntegrity para una respuesta en particular. El valor del parámetro contiene una lista de strings, como en el siguiente ejemplo:

{"evaluationType": "BASIC,HARDWARE_BACKED"}

Actualmente, los valores posibles son los siguientes:

BASIC
Se utilizaron mediciones y datos de referencia típicos.
HARDWARE_BACKED
Se utilizaron funciones de seguridad con copia de seguridad en hardware. Esto incluye funciones como las siguientes:Certificación de claves, que es compatible en dispositivos que se enviaron con Android 8.0 (nivel de API 26) y versiones posteriores.

En tu app, puedes tratar la presencia de HARDWARE_BACKED en el parámetro evaluationType como un indicador de una evaluación más sólida de la integridad del dispositivo.

Nota: Solo se recomienda basarse en el parámetro evaluationType en el caso de las apps que ya usan el veredicto ctsProfileMatch y que requieren el nivel más alto de garantías de integridad de dispositivos, incluso a costa de limitar su base de usuarios. En la mayoría de los casos, debes seguir dependiendo de los veredictos de basicIntegrity y ctsProfileMatch para detectar abusos. Estos veredictos ya incorporan funciones de seguridad respaldadas por hardware, cuando corresponda su uso.

Si decides depender de la presencia de un valor determinado en el parámetro evaluationType, deberías considerar implementar un mecanismo de reintento en tu app en caso de que haya errores temporales.

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 se haya enviado el certificado de hoja 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 la solicitud original. Específicamente, asegúrate de que se haya validado la marca de tiempo y de 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 tienes 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 campos nuevos (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. Específicamente, 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 para 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 APIs 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

Si quieres 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 APIs de SafetyNet o las usas, aceptas las Condiciones del Servicio de las APIs 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 APIs.

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 para garantizar la precisión y nos comprometemos a mejorar estos métodos con el tiempo para garantizar que continúen siendo precisos.

Aceptas cumplir con todas las leyes, reglamentaciones 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ás las APIs para alentar ni promover actividades ilegales o la violación de los derechos de terceros. No infringirás ninguna otra condición del servicio de Google (o sus afiliados).

Reconoces y comprendes que la API de SafetyNet funciona a través de la recopilación de información de hardware y software, como datos de dispositivos y aplicaciones, y resultados de certificaciones de SafetyNet, y que envía esos datos a Google para su análisis. Según el artículo 3(d) de las Condiciones del Servicio de las APIs de Google, aceptas que, si utilizas las APIs, eres responsable de brindar avisos o solicitar el consentimiento necesario para la recopilación y el uso compartido de estos datos con Google.

Seguridad de los datos de la certificación de SafetyNet

Google Play tiene una sección de Seguridad de los datos para que los desarrolladores divulguen las prácticas de seguridad, uso compartido y recopilación de datos de sus apps. Para ayudarte a completar los requisitos de la sección de Seguridad de los datos, puedes usar la siguiente información sobre cómo la API de SafetyNet Attestation controla los datos:

Tipo de práctica Cómo aplica la práctica la API de SafetyNet Attestation
Datos recopilados sobre el uso
  • Nombre de paquete
  • Certificado de firma de la aplicación
  • Un token de certificación de dispositivo generado por los Servicios de Google Play
Propósito de la recopilación de datos Los datos recopilados se usan para verificar la integridad de la aplicación y del dispositivo.
Encriptación de datos Los datos no están encriptados.
Datos compartidos Los datos no se transfieren a ningún tercero.
Eliminación de datos Los datos se borran después de un período de retención fijo.

Para completar tu divulgación de datos, puedes usar la guía sobre tipos de datos de Android para determinar qué tipo de datos recopilas. En la divulgación de datos, asegúrate de tener en cuenta la forma en que tu app específica comparte y usa los datos recopilados.

Si bien nuestro objetivo es ayudarte de la manera más transparente posible, no podemos hablar por ti. Eres el único responsable de decidir cómo responder el formulario de la sección de Seguridad de los datos de Google Play en relación con la recopilación de datos del usuario de tu app, el uso compartido y las prácticas de seguridad.