Seguridad · Android · Fintech
Play Integrity está bloqueando a tus clientes que pagan
Play Integrity de Google no responde si un dispositivo está comprometido. Responde una pregunta más estrecha, y esa brecha rechaza clientes que no puedes ver.

Un cliente escribe a soporte porque tu app no abre en su teléfono. Ese teléfono tiene el bootloader bloqueado y un sistema operativo verificado criptográficamente en cada arranque — más estricto, por casi cualquier medida, que la mitad de los equipos que tu app admite sin preguntar. Lo rechaza igual, porque tu app le preguntó a la Play Integrity API de Google si podía confiar en ese dispositivo. Play Integrity no responde esa pregunta. Su veredicto por defecto responde una más estrecha: ¿es esta una imagen certificada por un fabricante aprobado por Google? Son preguntas distintas, y la diferencia entre ambas está rechazando a gente que quiere pagarte.
La historia llegó a la prensa con la forma habitual: las apps bancarias bloquean sistemas Android endurecidos, GrapheneOS protesta, Revolut aparece con nombre. Así contado parece un asunto de un sistema operativo de nicho. No lo es. Toda app de fintech, marketplace y billetera en Android lleva una puerta de integridad, casi todas llevan la misma, y la eligió una sola vez quien conectó el SDK. Esa puerta decide quién tiene permiso de ser tu cliente.
Qué está verificado aquí y qué no
Abajo conviven dos tipos de afirmación, y no tienen el mismo peso.
Verificado en los documentos primarios el 24 de octubre de 2026: cada cita de la referencia de Play Integrity y del esquema de atestación del Android Open Source Project, y el archivo de lista permitida firmado — 1.452 bytes, 21 huellas de clave, firma Ed25519 comprobada localmente con ssh-keygen -Y verify.
Informado por una parte interesada y sin medición independiente: las cifras de titular. GrapheneOS afirma que alrededor del 10% de las apps bancarias no están disponibles para sus usuarios y que una docena de bancos ya empezó a permitirlo. Ambos números son del propio proyecto, publicados sin metodología ni muestra, y la lista colaborativa que enlaza en ese mismo mensaje nombra cuatro instituciones, no doce. Qué comprueba Revolut en concreto es también GrapheneOS leyendo una app de código cerrado — el medio que lo cubrió no afirma haber verificado las afirmaciones, y Revolut no se pronunció. Nada de esto toca el mecanismo, que está en la documentación de Google. Pero una cifra que una parte interesada publica sobre sí misma debe leerse como tal.

GrapheneOS, 23 de octubre de 2026. Ambas cifras son del propio proyecto; ninguna tiene medición independiente. Fuente: grapheneos.social.
El veredicto por defecto tiene dos salidas, y ninguna significa "comprometido"
Google documenta el deviceRecognitionVerdict por defecto con exactamente dos valores posibles. MEETS_DEVICE_INTEGRITY significa que "la app se ejecuta en un dispositivo Android genuino y certificado", con prueba respaldada por hardware en Android 13 o superior de "que el bootloader del dispositivo está bloqueado y que el Android cargado es una imagen certificada del fabricante". El otro valor es vacío — un campo en blanco — que Google documenta como "la app se ejecuta en un dispositivo con señales de ataque (como hooking de API) o de compromiso del sistema (como tener root)".
Léelas juntas. Un bootloader bloqueado que ejecuta un sistema verificado que no es de Google cumple la propiedad de seguridad de la primera frase y falla la propiedad de certificación de la misma frase, así que cae en el segundo cubo — junto a un teléfono con root y un framework de hooking enganchado. Tu regla antifraude no los distingue, porque la API nunca se lo dijo.
Y no hay un escalón más bajo que lo arregle. MEETS_BASIC_INTEGRITY suena al nivel de degradación elegante, pero Google lo define como un estado en el que "el bootloader del dispositivo puede estar bloqueado o desbloqueado, y el arranque puede estar verificado o no verificado". Es más débil, no más estrecho: aceptarlo para admitir dispositivos endurecidos admite también todo lo que la puerta existe para excluir.
Google publica una segunda API de atestación que sí responde la pregunta
Play Integrity está construida sobre la atestación de claves por hardware de Android, y la primitiva de abajo resulta más informativa que el producto que se apoya en ella. El esquema de AOSP devuelve una estructura RootOfTrust con cuatro campos separados:
RootOfTrust ::= SEQUENCE {
verifiedBootKey OCTET_STRING,
deviceLocked BOOLEAN,
verifiedBootState VerifiedBootState,
verifiedBootHash OCTET_STRING,
}
VerifiedBootState ::= ENUMERATED {
Verified (0),
SelfSigned (1),
Unverified (2),
Failed (3),
}

La misma empresa, otra API: RootOfTrust entrega a la app la clave de arranque, el estado del bloqueo y un veredicto SelfSigned como hechos separados. Fuente: esquema de atestación del Android Open Source Project.
SelfSigned es el escalón que Play Integrity no sabe expresar: la cadena de arranque verificada, bajo una clave de firma que no es la del fabricante. Es distinto de Unverified y de Failed, y llega junto a la clave que firmó — así que la app decide en qué claves no originales confía, en lugar de heredar la lista de un proveedor. Esta API existe desde Android 8.
| Play Integrity (veredicto por defecto) | Atestación de claves por hardware | |
|---|---|---|
| Pregunta que responde | ¿Es una imagen certificada por un fabricante aprobado por Google? | ¿Está verificada la cadena de arranque, y bajo qué clave? |
| Qué devuelve | MEETS_DEVICE_INTEGRITY o un valor vacío | verifiedBootKey, deviceLocked, verifiedBootState, verifiedBootHash |
| Bloqueado, verificado, clave no original | Vacío — idéntico a root bajo ataque | SelfSigned, más la clave, como hechos separados |
| Disponible desde | Android 8, vía Google Play services | Android 8, en hardware, sin depender de Play |
| Quién fija la política | Tú |
Y el argumento no es solo de quienes están siendo bloqueados. Una comparación académica de la Johannes Kepler University Linz concluye que la API de atestación por hardware "ofrece una alternativa más flexible, que habilita el soporte a sistemas operativos de terceros manteniendo garantías de seguridad fuertes mediante almacenamiento de claves respaldado por hardware", y que el carácter propietario de Play Integrity y su dependencia de Google Play Services "plantean preocupaciones de competencia".
El cliente rechazado no aparece en ninguno de tus números
Esto es lo que lo convierte en un problema de gestión y no de ingeniería. El cliente al que tu puerta rechaza no produce una transacción fallida: ni un pago rechazado, ni un ticket en una categoría que alguien revise, ni una caída en un paso del embudo que hayas instrumentado. La app se cerró antes de que nada de eso pudiera existir.
Así que el coste es real y no se mide, que es exactamente la razón por la que la puerta nunca se revisa. Cualquier otro control de tu producto tiene una tasa de falsos positivos que alguien discute en una reunión. La de esta es desconocida, y las personas que están dentro son la única cohorte que tu analítica no puede ver.
Es decir: nadie en tu empresa ha preguntado si deberíais admitir a estos usuarios. Lo respondió una dependencia.
Cuánto cuesta arreglarlo
La guía para desarrolladores del sistema Android endurecido más común resume la comprobación en dos frases: exige que verifiedBootState sea Verified o SelfSigned y, en el caso SelfSigned, comprueba que verifiedBootKey coincida con una de las claves de arranque verificado publicadas.
Descargué esa lista. Es un archivo JSON de 1.452 bytes con 21 huellas de clave, cada una una cadena hexadecimal de 64 caracteres, con marca de tiempo del 20 de abril de 2026, acompañado de una firma OpenSSH y una clave de firmante publicada. Verificarlo es un comando:
ssh-keygen -Y verify -f allowed_signers -I contact@grapheneos.org \
-n 'attestation keys' -s attestation.json.sig < attestation.json
Sale con código 0 y firma Ed25519 válida. Lo ejecuté el 24 de octubre de 2026.

La integración, en dos frases, tomada de la guía del desarrollador: comprobar una enumeración y comparar un hash con un archivo firmado de 1,4 KB. Fuente: grapheneos.org.
Sé honesto sobre el tamaño del trabajo. Si tu app ya hace atestación de claves por hardware, admitir estos dispositivos son dos campos que ya recibes hoy. Si solo llama a Play Integrity, el trabajo es adoptar la API que está debajo de la que ya llamas — un sprint de forma conocida, contra documentación que existe desde hace ocho años. No es un proyecto de investigación ni una imposibilidad, que son las dos cosas que suelen alegarse.
Algunos bancos ya lo hicieron
La lista colaborativa de compatibilidad de PrivSec.dev tiene 907 entradas únicas de apps. Dos están anotadas como haber permitido el sistema endurecido mediante la API de atestación por hardware — Swissquote y Yuh — y dos como dándole soporte oficial: UBS (con UBS key4) y AmeriCU Credit Union.

Lista de compatibilidad mantenida por la comunidad, leída el 24 de octubre de 2026: dos bancos anotados como permitidos vía la API de atestación por hardware, uno con soporte oficial. Fuente: PrivSec.dev; GrapheneOS no garantiza la exactitud de la lista.
Trata esas entradas por lo que son: reportes de usuarios. GrapheneOS, que enlaza la lista, dice con claridad que "no ofrece ninguna garantía respecto a la validez de la lista", y ningún banco mencionado aquí nos ha dicho qué API de atestación usa ni por qué. Lo que la lista establece no es la hoja de ruta de un proveedor. Establece que la cosa es entregable, porque alguien la entregó.
La misma lista cuenta la fricción en la dirección contraria: 8 entradas en las que la app no puede instalarse desde Google Play por causa de Play Integrity, 4 en las que solo funciona si se bloquea la API, y una — N26 — en la que Play Integrity bloquea nuevos registros mientras las cuentas existentes siguen funcionando. Esa última merece un minuto, y es la más relevante para cualquiera que use banca digital en España. Es una puerta que prohíbe la adquisición y permite la retención, lo contrario de cómo alguien escribiría esa política a propósito.
La decisión, dicha como decisión
Tres preguntas, y son de quien responde por el producto, no de quien responde por el build móvil.
- ¿Qué necesitamos saber sobre un dispositivo? Si la respuesta es "que la cadena de arranque está intacta", estás llamando a la API equivocada. Si es "que es una imagen certificada por Google", dilo en voz alta — esa es una posición comercial, no de seguridad.
- ¿Cuál es nuestra política de falsos positivos? Elige un número. "Rechazamos dispositivos endurecidos y asumimos los registros perdidos" es defendible. "Nunca lo miramos" no lo es.
- ¿Quién es el dueño de esta comprobación? Di un nombre. Un control sin dueño se mueve con los valores por defecto de su proveedor, y te enteras cuando te lo cuenta un cliente.
Vemos esta forma todo el tiempo en el trabajo que llega para pasar a producción: una dependencia añadida por una buena razón, conectada a una decisión que nadie escribió, y luego tratada como zanjada porque compila. El mismo fallo que entregar una demo como si fuera producto (el problema del Roadster), el mismo que dejar que código generado defina una frontera que nadie revisó (contener el código escrito por IA). La puerta de integridad es un caso inusualmente caro, porque el error es silencioso y recae sobre los clientes que más se preocupan por proteger su propio dispositivo.
Puntos clave
- El veredicto de dispositivo por defecto de Play Integrity tiene dos salidas: imagen certificada de fabricante, o un valor vacío que Google documenta como señales de ataque o compromiso del sistema.
- Un teléfono con bootloader bloqueado que ejecuta un sistema verificado que no es de Google recibe el valor vacío — indistinguible de root bajo ataque.
- La atestación de claves por hardware, sobre la que Play Integrity está construida, devuelve
verifiedBootStatecon un valorSelfSigneddistinto más la clave de firma, de modo que la app fija su propia política. - El cliente rechazado no genera transacción, ticket ni evento de embudo, así que el coste de la puerta nunca llega a un número revisado — y por eso nunca se revisa.
Preguntas frecuentes
¿Play Integrity le dice a mi app si un dispositivo tiene root? No de forma distinguible. Google agrupa el root con otros fallos en un único veredicto vacío que también cubre cualquier sistema que no sea una imagen certificada de fabricante. Si tu regla necesita "con root" en concreto, el veredicto por defecto no te lo da.
¿Aceptar MEETS_BASIC_INTEGRITY es un término medio razonable?
No. Google define ese nivel como permitiendo un bootloader bloqueado o desbloqueado y un arranque verificado o no verificado. Es estrictamente más débil, así que admite justo lo que querías excluir.
¿A cuántos clientes admitiría esto en la práctica? Nadie publica ese número, y ese es el punto del artículo. Se puede medir: registra los campos de atestación que ya recibes, cuenta los dispositivos bloqueados y verificados bajo una clave no original, y tendrás el tamaño de la cohorte antes de decidir nada.