wandres.dev
PERMISOS Y PRIVACIDAD · el contrato con el usuario

Privacidad de plataforma y minimización

El permiso concedido es solo la puerta de entrada; lo que ocurre después con el dato es donde se juega la privacidad real. Esta lección cierra el nivel con la capa que rodea al código: el manifiesto de datos de Google Play y la obligación de que la declaración coincida con el comportamiento observable, incluidas las bibliotecas de terceros; la arquitectura de Privacy Sandbox en Android y el desmantelamiento del identificador transversal; superficies de información que no parecen datos, como la visibilidad de paquetes; las herramientas de auditoría del propio sistema; y por qué la minimización es la única técnica de privacidad que no caduca.

⏱ 22 min

Una aplicación puede tener impecable cada diálogo de permiso de este nivel y ser, aun así, un desastre de privacidad. El permiso regula el acceso a un dato en un instante; no dice nada sobre cuánto tiempo lo conservas, con quién lo compartes, qué biblioteca de análisis lo reenvía a un tercero sin que tú lo sepas, ni qué se puede reconstruir cruzando lo que recoges con lo que recogen otros. Esa segunda mitad del problema no la gobierna el sistema operativo sino un entramado de declaraciones contractuales ante la tienda, arquitecturas de aislamiento en construcción y obligaciones legales que varían por jurisdicción. Es la parte del trabajo que no compila, que no se puede verificar con un test y que sin embargo determina si tu aplicación seguirá publicada dentro de un año. Y tiene una particularidad que conviene enunciar desde el principio: es la única parte donde la respuesta técnicamente correcta y la respuesta comercialmente conveniente coinciden casi siempre, porque el dato que no recoges no se filtra, no se declara, no se audita y no cuesta nada almacenar.

🎯 Al terminar esta lección sabrás
  • Rellenar y mantener el manifiesto de datos de Google Play de modo que coincida con el comportamiento real, bibliotecas incluidas.
  • Explicar la arquitectura de Privacy Sandbox en Android y qué sustituye al identificador publicitario transversal.
  • Identificar superficies de información no evidentes, como la visibilidad de paquetes, y acotarlas.
  • Auditar el acceso a datos privados desde tu propio proceso y aplicar minimización como criterio de diseño.

El manifiesto de datos: una declaración verificable

Toda aplicación publicada en Google Play debe declarar, en la sección de seguridad de los datos, qué información recoge, cuál comparte con terceros, con qué finalidad, si es obligatoria u opcional, si viaja cifrada y cómo puede el usuario solicitar su eliminación. Esa declaración se convierte en una ficha pública visible antes de instalar, y es la traducción a producto de la idea de consentimiento informado.

La propiedad crítica de ese documento es que no es una manifestación de intenciones sino una afirmación comprobable. Google ejecuta análisis estáticos y dinámicos sobre el paquete publicado, observa el tráfico saliente y compara lo que ve con lo que declaraste. Una divergencia se trata como incumplimiento de política, con consecuencias que van desde el bloqueo de la actualización hasta la retirada de la ficha.

La fuente número uno de divergencias no es la mala fe sino la ignorancia sobre el propio binario. Una biblioteca de análisis, una de publicidad, un recolector de errores o un motor de recomendación recogen datos por su cuenta, y esa recogida es tuya a todos los efectos declarativos. Nadie preguntará si sabías lo que hacía la dependencia que integraste; el paquete lleva tu firma.

📋

Recoger frente a compartir

Recoger es sacar el dato del dispositivo hacia tus servidores. Compartir es entregarlo a un tercero. Procesar en el dispositivo y descartar sin transmitir no es ninguna de las dos cosas, y esa distinción es el mayor incentivo técnico que existe hoy para llevar cómputo al terminal.

🔎

Auditar las dependencias

El índice de SDK de Google publica el comportamiento declarado de las bibliotecas más usadas. Revisarlo antes de añadir una dependencia cuesta diez minutos y evita descubrir en una revisión que tu aplicación comparte identificadores que no sabías que existían.

Conviene además tratar el manifiesto de datos como código: revisarlo en cada actualización de dependencias, no solo cuando cambia una funcionalidad propia, y anotar junto a cada dependencia qué categoría de datos aporta a la declaración. Una entrada olvidada tras una actualización de biblioteca es indistinguible, desde fuera, de una ocultación deliberada.

Dos casillas de ese formulario merecen atención especial porque casi nadie las lee con cuidado y ambas cambian por completo la lectura que hace el usuario de tu ficha. La primera es la que distingue entre datos obligatorios y opcionales: marcar como opcional algo que en realidad bloquea la funcionalidad si se rechaza es una declaración falsa, aunque el diálogo técnico sea el mismo. La segunda es la del tratamiento efímero, que solo se puede marcar si el dato se procesa en memoria y se descarta sin persistirse en ningún sistema, ni siquiera en un registro de servidor.

💡
Procesar en el dispositivo cambia la categoría, no solo el rendimiento

Un modelo de reconocimiento que corre en el terminal y devuelve una etiqueta, en lugar de enviar la fotografía a un servidor, no es únicamente más rápido y más barato: es una aplicación distinta desde el punto de vista declarativo, porque no recoge imágenes. Esa diferencia se propaga a la ficha pública, al alcance de una auditoría, a la superficie regulatoria y al daño potencial de una brecha. La inferencia en el dispositivo dejó de ser una optimización técnica para convertirse en una decisión de arquitectura de privacidad con consecuencias contractuales.

Privacy Sandbox: desmontar el identificador transversal

Durante quince años, la publicidad móvil se construyó sobre un identificador estable y común a todas las aplicaciones del dispositivo, que permitía reconstruir el comportamiento completo de una persona cruzando datos de decenas de proveedores. La plataforma lleva varias versiones desactivando esa capacidad por etapas.

La primera etapa fue restringir el propio identificador: el usuario puede borrarlo, en cuyo caso todas las lecturas devuelven ceros, y desde la API 33 hay que declarar un permiso específico para leerlo. Construir una funcionalidad sobre un valor que puede ser una cadena de ceros en una fracción notable de los dispositivos es, sencillamente, mala ingeniería.

La segunda etapa es Privacy Sandbox, un conjunto de interfaces que persiguen resolver los casos de uso legítimos de la publicidad sin identificadores compartidos. Su pieza estructural es el entorno de ejecución para bibliotecas, que traslada los SDK de terceros a un proceso separado con sus propios permisos y sin acceso a los datos de la aplicación anfitriona. Sobre esa base se apoyan las interfaces de temas de interés, de audiencias protegidas y de informe de atribución, todas ellas diseñadas para devolver agregados o señales de baja resolución en lugar de identidades.

flowchart TD
A[Modelo antiguo] --> B[Identificador comun a todas las aplicaciones]
B --> C[Perfil unico reconstruido por terceros]
D[Modelo nuevo] --> E[SDK en proceso aislado]
D --> F[Temas de interes agregados]
D --> G[Informe de atribucion con ruido]
E --> H[Sin acceso a datos de la aplicacion]
F --> I[Sin identidad transversal]
G --> I

Vale la pena entender el mecanismo de cada pieza, aunque los nombres cambien. La interfaz de temas deriva en el dispositivo un puñado de categorías de interés a partir del uso, las rota periódicamente y entrega solo unas pocas por consulta, de modo que ningún observador puede reconstruir un perfil estable. La de audiencias protegidas mantiene la pertenencia a grupos dentro del terminal y ejecuta la subasta localmente, sin que el anunciante llegue a saber quién estaba en el grupo. Y la de informes de atribución sustituye el seguimiento de conversiones individuales por agregados con ruido calibrado y un presupuesto de privacidad que limita cuántas consultas puede hacer cada participante.

Ese último detalle —el presupuesto— es el concepto más interesante de todo el conjunto y el que menos se comenta, porque introduce en una API comercial una idea procedente de la privacidad diferencial: la cantidad de información que se puede extraer de una población está acotada por diseño, y una vez agotada la cuota no hay manera de obtener más consultando de otra forma. Es un cambio de naturaleza respecto al modelo anterior, donde la única cota era la capacidad de almacenamiento de quien recogía.

La lectura estratégica importa más que los detalles de cada interfaz, que seguirán cambiando. La dirección es la sustitución sistemática del dato individual por el agregado, del identificador por la señal ruidosa, y del acceso directo por la mediación del sistema. Cualquier arquitectura de producto que dependa de reconocer a la misma persona a través de aplicaciones distintas está apostando contra esa dirección.

Superficies que no parecen datos

Hay información sensible que no atraviesa ningún permiso y que la plataforma ha ido cerrando con mecanismos propios. La más instructiva es la lista de aplicaciones instaladas, que durante años fue libremente consultable y que resultó ser uno de los vectores de perfilado más potentes que existían: la combinación de aplicaciones de una persona revela religión, orientación, situación económica, estado de salud y afiliación política con una precisión incómoda.

Desde la API 30, una aplicación solo ve los paquetes que declara necesitar, mediante el elemento queries del manifiesto, o los que responden a los tipos de interacción que declara. El permiso que devuelve la visibilidad completa está restringido en la tienda a casos concretos como los gestores de aplicaciones o los antivirus.

<queries>
    <package android:name="com.example.socio" />
    <intent>
        <action android:name="android.intent.action.SEND" />
        <data android:mimeType="image/*" />
    </intent>
</queries>

La restricción tiene un efecto secundario que sorprende a quien la encuentra por primera vez: si tu aplicación abre otra mediante una acción concreta y no la ha declarado, la comprobación previa de que existe un destinatario devolverá que no hay ninguno, aunque la aplicación esté instalada. El síntoma es un botón que informa de que no hay ninguna aplicación capaz de abrir el fichero en un dispositivo donde evidentemente la hay, y la causa está a diez líneas del manifiesto.

En la misma familia de superficies invisibles entran los identificadores de hardware no reiniciables, hoy inaccesibles sin privilegios de sistema, y la resolución de nombres de red o los tiempos de respuesta, que permiten inferencias laterales. La regla que ordena todo el conjunto es que cualquier señal estable y común a varias aplicaciones acabará siendo tratada como identificador, con independencia de que se diseñara para eso.

El propio sistema ofrece la herramienta para mirarse por dentro: la interfaz de auditoría de acceso a datos permite registrar una devolución de llamada que se dispara cada vez que algún componente del proceso accede a un dato protegido, indicando la etiqueta de atribución responsable. Es el único mecanismo práctico para descubrir qué hacen realmente tus dependencias.

Para que esa etiqueta signifique algo hay que declararla previamente en el manifiesto y asociarla a un contexto derivado desde el que se ejecuta cada subsistema. Sin ese trabajo previo, la auditoría te dirá que tu aplicación accedió a la ubicación, que es exactamente lo que ya sabías; con él, te dirá qué parte de tu aplicación lo hizo, que es lo que necesitas para responder a una pregunta de cumplimiento o para explicar una anomalía en el panel de privacidad del usuario.

<attribution
    android:tag="analitica"
    android:label="@string/atribucion_analitica" />
<attribution
    android:tag="mapa"
    android:label="@string/atribucion_mapa" />
val gestor = getSystemService(AppOpsManager::class.java)
gestor?.setOnOpNotedCallback(
    mainExecutor,
    object : AppOpsManager.OnOpNotedCallback() {
        override fun onNoted(op: SyncNotedAppOp) = registrar(op.op, op.attributionTag)
        override fun onSelfNoted(op: SyncNotedAppOp) = registrar(op.op, op.attributionTag)
        override fun onAsyncNoted(op: AsyncNotedAppOp) = registrar(op.op, op.attributionTag)
    },
)
⚠️
Seudonimizar no es anonimizar

Aplicar una función resumen a un correo electrónico o a un identificador de dispositivo no produce un dato anónimo: produce el mismo dato con otro nombre, porque el espacio de entradas es enumerable y la correspondencia es estable entre sistemas, que es justamente lo que hace útil un identificador. Los marcos regulatorios lo tratan como dato personal seudonimizado y las políticas de la tienda también. La única transformación que elimina de verdad el riesgo es la que destruye información: agregar, truncar, añadir ruido calibrado o no recoger.

Minimizar: la única técnica que no caduca

Todo lo anterior son mecanismos, y los mecanismos cambian con cada versión. Hay un principio que no cambia nunca y que subsume a todos: recoge la menor cantidad de datos, con la menor precisión, durante el menor tiempo que permita cumplir la función. Cada una de las tres dimensiones se puede recorrer por separado y las tres reducen simultáneamente la superficie regulatoria, el coste de infraestructura y el daño potencial de una brecha.

Reducir la cantidad significa cuestionar cada campo del evento que registras. Reducir la precisión significa preguntarse si necesitas la coordenada o basta el código postal, si necesitas la fecha de nacimiento o basta el intervalo de edad, si necesitas el identificador o basta un contador. Y reducir el tiempo significa fijar una caducidad explícita para cada tabla, porque el dato que se conserva por si acaso es exactamente el que aparece en la filtración.

Existe además una obligación que muchos equipos descubren tarde: Google Play exige que las aplicaciones con cuentas de usuario ofrezcan una vía de eliminación de la cuenta y de los datos asociados, accesible desde dentro de la aplicación y también desde la web para quien ya la desinstaló. Implementar el borrado real, con propagación a copias y a sistemas analíticos, es un trabajo de arquitectura que conviene planificar y no improvisar bajo la presión de una fecha de cumplimiento.

La prueba de fuego de una arquitectura minimizada es sorprendentemente sencilla de formular y muy incómoda de superar: si mañana tuvieras que borrar por completo a un usuario concreto, ¿sabrías enumerar todos los lugares donde vive alguno de sus datos? La mayoría de los sistemas no lo sabe, y esa ignorancia no es un problema de documentación sino el síntoma de que la recogida creció sin gobierno. Un equipo que puede responder esa pregunta en cinco minutos tiene, casi por construcción, un sistema con menos datos, menos copias y menos integraciones opacas.

El último eslabón, y el que más se descuida, es la comunicación. Una política de privacidad de nueve mil palabras redactada para protegerte de un litigio no informa a nadie y no cuenta como consentimiento informado en ningún marco serio. Lo que sí funciona es la explicación de una frase, situada exactamente donde ocurre la recogida, escrita en el idioma del usuario y no en el del departamento jurídico. Ese texto breve es, en la práctica, la interfaz real de tu política de datos, y la parte extensa es su apéndice.

El dato que no existe es el único que nunca te traiciona

Conviene cerrar el nivel con la proposición que ordena todo lo demás y que casi nunca se enuncia con la crudeza suficiente, porque es incómoda para la manera en que se construyen los productos digitales. Cada dato que una organización recoge es un pasivo con vencimiento indeterminado, y se comporta económicamente como tal: genera un valor presente, normalmente pequeño y a menudo hipotético, y acumula un riesgo futuro que crece con el tiempo de retención, con el número de sistemas que lo replican, con la rotación del personal que conoce su existencia y con la evolución de un marco regulatorio que nadie puede prever a cinco años. Los equipos aprenden a estimar bien el numerador de esa fracción, porque el valor es medible y llega pronto; sistemáticamente no saben estimar el denominador, porque el coste es diferido, probabilístico y lo paga otro. De ahí surge el sesgo estructural que produce las bases de datos que todo el mundo tiene y nadie ha auditado: tablas de eventos con doscientas columnas de las que se consultan seis, registros históricos sin política de caducidad porque quizá algún día sirvan para entrenar algo, coordenadas guardadas con siete decimales cuando la funcionalidad opera a nivel de ciudad. Ninguna de esas decisiones se tomó nunca de forma explícita; todas son el residuo de haber elegido, muchas veces y por defecto, la opción de guardar por si acaso. La disciplina contraria es sencilla de enunciar y difícil de sostener, porque exige decir que no a peticiones razonables: ningún campo entra en el registro sin una pregunta concreta que solo él responda, ninguna tabla nace sin fecha de caducidad, ninguna precisión supera a la que la funcionalidad consume, y toda petición de recoger algo nuevo compite con la carga que ya llevas en lugar de sumarse gratis. Su recompensa es que resuelve simultáneamente cuatro problemas que normalmente se atacan por separado: la conformidad regulatoria, porque no hay nada que declarar; la seguridad, porque no hay nada que filtrar; el coste, porque no hay nada que almacenar; y la confianza, porque no hay nada que explicar. Y tiene una propiedad que ninguna otra técnica de este nivel posee: es indiferente a la versión de Android, a la política de la tienda que se publique el año que viene y a la jurisdicción del usuario. Los mecanismos caducan; el dato que no existe, no.

⚔️ Reduce tu superficie de datos
  1. Reconstruye el manifiesto de datos desde cero mirando el tráfico real de la aplicación en lugar de la declaración anterior, y anota cada divergencia.
  2. Lista tus dependencias de terceros y localiza, para cada una, qué datos recoge por su cuenta; elimina la que no puedas justificar.
  3. Activa la auditoría de acceso a datos durante una sesión completa y clasifica cada acceso registrado en propio, de biblioteca o inexplicable.
  4. Revisa el elemento queries de tu manifiesto y comprueba que no consultas paquetes que ya no usas.
  5. Elige la tabla de eventos más grande de tu sistema analítico, retira todos los campos que nadie ha consultado en seis meses y fija una caducidad explícita para el resto.