Publicado por Oscar Rodríguez, ingeniero de relaciones con los desarrolladores
Con el reciente lanzamiento del API de integridad de reproducciónmás desarrolladores ahora están tomando medidas para proteger sus juegos y aplicaciones de interacciones potencialmente riesgosas y fraudulentas.
Además de las señales útiles sobre el estado de la aplicación, el estado del dispositivo y la información de la licencia, Play Integrity API presenta una característica simple pero muy útil llamada “nonce” que, cuando se usa correctamente, puede fortalecer aún más las protecciones existentes. además de mitigar ciertos tipos de ataques, como ataques de manipulación de persona en el medio (PITM) y ataques repetidos.
En esta publicación de blog, analizaremos más a fondo qué es el nonce, cómo funciona y cómo se puede usar para proteger aún más su aplicación.
¿Qué es un nonce?
En criptografía e ingeniería de seguridad, un nonce (número una vez) es un número que se utiliza una sola vez en una comunicación segura. Hay muchas aplicaciones para nonce, como autenticación, cifrado y hash.
En la API Play Integrity, el nonce es un blob binario opaco codificado en base 64 que configura antes de invocar la verificación de integridad de la API y se devolverá tal cual dentro de la respuesta firmada de la API. Dependiendo de cómo cree y valide su nonce, puede aprovecharlo para fortalecer aún más las protecciones existentes que ofrece Play Integrity API, así como para mitigar ciertos tipos de ataques, como los ataques de manipulación de persona en el medio (PITM). y repeticiones ataques.
Aparte de devolver el nonce tal como está en la respuesta firmada, Play Integrity API no procesa los datos reales del nonce, por lo que, siempre que sea un valor de base 64 válido, puede establecer cualquier valor arbitrario. Dicho esto, para firmar digitalmente la respuesta, el nonce se envía a los servidores de Google, por lo que es muy importante no configurar el nonce para ningún tipo de información de identificación personal (PII), como el nombre, el teléfono o la dirección de correo electrónico del ‘usuario’. .
Configurando el nonce
Después configura tu aplicación para usar la API Play IntegrityTú establecer el momento con el setNonce()
o su variante adecuada, disponible en las versiones Kotlin, Java, Unity y Native de la API.
Kotlin:
val nonce: String = ... // Create an instance of a manager. val integrityManager = IntegrityManagerFactory.create(applicationContext) // Request the integrity token by providing a nonce. val integrityTokenResponse: Task<IntegrityTokenResponse> = integrityManager.requestIntegrityToken( IntegrityTokenRequest.builder() .setNonce(nonce) // Set the nonce .build())
Java:
String nonce = ... // Create an instance of a manager. IntegrityManager integrityManager = IntegrityManagerFactory.create(getApplicationContext()); // Request the integrity token by providing a nonce. Task<IntegrityTokenResponse> integrityTokenResponse = integrityManager .requestIntegrityToken( IntegrityTokenRequest.builder() .setNonce(nonce) // Set the nonce .build());
Unidad:
string nonce = ... // Create an instance of a manager. var integrityManager = new IntegrityManager(); // Request the integrity token by providing a nonce. var tokenRequest = new IntegrityTokenRequest(nonce); var requestIntegrityTokenOperation = integrityManager.RequestIntegrityToken(tokenRequest);
Nativo:
/// Create an IntegrityTokenRequest object. const char* nonce = ... IntegrityTokenRequest* request; IntegrityTokenRequest_create(&request); IntegrityTokenRequest_setNonce(request, nonce); // Set the nonce IntegrityTokenResponse* response; IntegrityErrorCode error_code = IntegrityManager_requestIntegrityToken(request, &response);
Verificación del nonce
La respuesta de la API Play Integrity se devuelve en forma de Token web JSON (JWT)cuya carga útil es un Texto JSON a texto sin formatoen el siguiente formato:
{ requestDetails: { ... } appIntegrity: { ... } deviceIntegrity: { ... } accountDetails: { ... } }
El nonce se puede encontrar dentro del requestDetails
estructura, con el siguiente formato:
requestDetails: { requestPackageName: "...", nonce: "...", timestampMillis: ... }
el valor de la nonce
el campo debe coincidir exactamente con lo que pasó a la API anteriormente. Además, debido a que el nonce está dentro de la respuesta firmada criptográficamente de la API de Play Integrity, no puede cambiar su valor después de recibir la respuesta. Al aprovechar estas propiedades, puede usar el nonce para proteger aún más su aplicación.
Protección de operaciones de alto valor
Consideremos el escenario en el que un atacante interactúa con un juego en línea que informa la puntuación del jugador al servidor del juego. En este caso, el dispositivo no está comprometido, pero el usuario puede ver y modificar el flujo de datos de la red entre el juego y el servidor con la ayuda de un servidor proxy o una VPN, para que el atacante pueda reportar una puntuación más alta, mientras que el verdadero la puntuación es mucho más baja.
Simplemente llamar a Play Integrity API no es suficiente para proteger la aplicación en este caso: el dispositivo no está comprometido y la aplicación es legítima, por lo que todas las comprobaciones realizadas por Play Integrity API fallarán.
Sin embargo, puede aprovechar el nonce de la API Play Integrity para asegurar esta operación particular de señalización de puntaje de juego de alto valor codificando el valor de la operación dentro del nonce. La implementación es la siguiente:
- El usuario inicia la acción de alto valor.
- Tu app prepara un mensaje que quiere proteger, por ejemplo, en formato JSON.
- Tu aplicación calcula un hash criptográfico del mensaje que quiere proteger. Por ejemplo, con los algoritmos hash SHA-256 o SHA-3-256.
- Su aplicación llama a la API Play Integrity y llama
setNonce()
para establecer el campo nonce en el hash criptográfico calculado en el paso anterior. - Su aplicación envía tanto el mensaje que desea proteger como el resultado firmado de la API Play Integrity a su servidor.
- El servidor de aplicaciones verifica que el hash criptográfico del mensaje recibido coincida con el valor del campo nonce en el resultado firmado y rechaza cualquier resultado que no coincida.
El siguiente diagrama de secuencia ilustra estos pasos:
Siempre que el mensaje original a proteger se envíe junto con el resultado firmado y tanto el servidor como el cliente utilicen exactamente el mismo mecanismo para calcular el nonce, esto ofrece una fuerte garantía de que el mensaje no ha sido alterado.
Tenga en cuenta que en este escenario, el modelo de seguridad funciona suponiendo que el ataque ocurre en la red, no en el dispositivo o la aplicación, por lo que es especialmente importante verificar también las señales de estado del dispositivo y la aplicación que ofrece API Play Integrity.
Prevenir ataques repetidos
Consideremos otro escenario en el que un atacante intenta interactuar con una aplicación cliente-servidor protegida por Play Integrity API, pero quiere hacerlo con un dispositivo comprometido, por lo que el servidor no lo detectará.
Para hacer esto, el atacante primero usa la aplicación con un dispositivo legítimo y recopila la respuesta firmada de la API Play Integrity. Luego, el atacante usa la aplicación con el dispositivo comprometido, intercepta la llamada a la API Play Integrity y, en lugar de realizar comprobaciones de estado, simplemente devuelve la respuesta firmada registrada previamente.
Dado que la respuesta firmada no se modificó de ninguna manera, la firma digital aparecerá bien y el servidor de la aplicación puede ser engañado para que piense que se está comunicando con un dispositivo legítimo. Esto se llama un ataque de replicación.
La primera línea de defensa contra tal ataque es verificar la timestampMillis
campo en la respuesta firmada. Este campo contiene la marca de tiempo cuando se creó la respuesta y puede ser útil para detectar respuestas sospechosas antiguas, incluso cuando la firma digital se verifique como genuina.
Dicho esto, también es posible aprovechar el nonce en Play Integrity API para asignar un valor único a cada respuesta y verificar que la respuesta coincida con el valor único establecido previamente. La implementación es la siguiente:
- El servidor crea un valor único a nivel mundial de una manera que los atacantes no pueden predecir. Por ejemplo, un número aleatorio criptográficamente seguro de al menos 128 bits.
- La aplicación llama a la API Play Integrity y establece el campo nonce en el valor único recibido del servidor de la aplicación.
- Su aplicación envía el resultado firmado de Play Integrity API a su servidor.
- Su servidor verifica que el campo nonce en el resultado firmado coincida con el valor único generado anteriormente y rechaza cualquier resultado que no coincida.
El siguiente diagrama de secuencia ilustra estos pasos:
Con esta implementación, cada vez que el servidor le pide a la aplicación que llame a la API Play Integrity, lo hace con un valor único global diferente, por lo que mientras el atacante no pueda predecir este valor, no se puede reutilizar una respuesta anterior. no coincidirá con el valor esperado.
Combinando ambas protecciones
Aunque los dos mecanismos descritos anteriormente funcionan de maneras muy diferentes, si una aplicación solicita ambas protecciones al mismo tiempo, puede combinarlas en una sola llamada a la API Play Integrity, por ejemplo, agregando los resultados de ambas protecciones en una base. 64. no hay. Una implementación que combina ambos enfoques es la siguiente:
- El usuario inicia la acción de alto valor.
- La aplicación le pide al servidor un valor único para identificar la solicitud
- Su servidor de aplicaciones genera un valor único a nivel mundial de una manera que los atacantes no pueden predecir. Por ejemplo, puede usar un generador de números aleatorios criptográficamente seguro para crear ese valor. Se recomienda que cree valores de 128 bits o superiores.
- El servidor de aplicaciones envía el valor único global a la aplicación.
- Tu app prepara un mensaje que quiere proteger, por ejemplo, en formato JSON.
- Tu aplicación calcula un hash criptográfico del mensaje que quiere proteger. Por ejemplo, con los algoritmos hash SHA-256 o SHA-3-256.
- La aplicación crea una cadena al agregar el valor único recibido del servidor de la aplicación y el hash del mensaje que desea proteger.
- Su aplicación llama a Play Integrity API y llama a setNonce () para establecer el campo nonce en la cadena creada en el paso anterior.
- Su aplicación envía tanto el mensaje que desea proteger como el resultado firmado de la API Play Integrity a su servidor.
- El servidor de aplicaciones divide el valor del campo nonce y verifica que el hash criptográfico del mensaje, así como el valor único generado anteriormente, coincidan con los valores esperados y rechaza cualquier resultado que no coincida.
El siguiente diagrama de secuencia ilustra estos pasos:
Estos son algunos ejemplos de formas en que puede usar el nonce para proteger aún más su aplicación de usuarios maliciosos. Si su aplicación maneja datos confidenciales o es vulnerable al abuso, esperamos que considere tomar medidas para mitigar estas amenazas con la ayuda de Play Integrity API.
Para obtener más información sobre el uso de Play Integrity API y para comenzar, visite la documentación en g.co/play/integrityapi.