Privacidad en la analítica: mínimo, anónimo y declarado
Medir y respetar a quien usa tu aplicación no son objetivos en conflicto, pero conciliarlos exige decisiones explícitas que casi nadie toma por omisión. Esta lección recorre el principio de minimización aplicado evento a evento, explica qué identifica de verdad y por qué el hash no anonimiza, detalla el manifiesto de privacidad con sus motivos de acceso y firmas de dependencias, y define un consentimiento que sea operable y no un cartel decorativo.
La conversación sobre privacidad en aplicaciones móviles suele degenerar en dos posturas igual de improductivas: la que asume que cualquier medición es una intrusión y renuncia a saber nada, y la que trata el asunto como un trámite administrativo que se resuelve marcando casillas la semana antes de publicar. Ninguna de las dos sobrevive al contacto con el trabajo real, porque el problema no es filosófico sino de ingeniería: hay que decidir, para cada campo de cada evento, si su presencia mejora una decisión lo bastante como para justificar su existencia, y hay que poder sostener esa decisión ante una revisión externa, ante un requisito regulatorio y ante la persona cuyos datos son. Lo interesante es que la disciplina que resulta de tomarse eso en serio produce, casi siempre, un sistema de medición mejor: más pequeño, más comprensible y más difícil de romper.
- Aplicar el principio de minimización campo a campo y sustituir valores exactos por tramos, banderas y agregados.
- Distinguir dato identificativo, seudónimo y anónimo, y entender por qué una función resumen no anonimiza nada.
- Escribir el manifiesto de privacidad con sus tipos de datos recogidos, motivos de acceso a
APIy firmas de dependencias. - Diseñar un consentimiento operable: granular, revocable, verificable en pruebas y con borrado real.
Minimizar es una decisión por campo
El principio suena abstracto hasta que se aplica a una línea concreta de código, y entonces se vuelve incómodamente específico. La pregunta no es si tu aplicación respeta la privacidad en general, sino si esta propiedad de este evento existe porque alguien va a tomar una decisión distinta según su valor. Un evento de búsqueda que incluye el término buscado casi nunca supera esa prueba: lo que el equipo necesita saber es si la búsqueda devolvió resultados, cuántos y cuánto tardó, no qué escribió esa persona. Sustituir el término por la longitud en tramos, el recuento de resultados y una bandera de si hubo interacción posterior responde exactamente a las mismas preguntas de producto y elimina de raíz una categoría entera de riesgo.
Ese ejercicio de sustitución tiene un catálogo corto de transformaciones que resuelven la mayoría de los casos. Un valor exacto se convierte en un tramo, y los tramos se eligen de forma que ninguno sea tan estrecho que aísle a una persona. Una marca de tiempo precisa se redondea a la hora o al día, porque la precisión de milisegundos raramente sirve para nada y en cambio permite correlacionar entre conjuntos de datos. Una ubicación se degrada a país o región. Un texto libre se sustituye por una categoría de un conjunto cerrado. Y un identificador estable se sustituye por uno que se regenera al cerrar sesión o al revocar el consentimiento.
Tramos, no valores
Longitud, duración, tamaño y recuento en cubos declarados. Se conserva la capacidad de comparar y se pierde la capacidad de reconocer.
Tiempo grueso
Redondeo a hora o a día. La precisión fina es la que permite cruzar tu conjunto de datos con otro y deshacer cualquier seudonimización.
Banderas, no contenido
Si hubo resultados, si hubo error, si había conexión. Casi todas las preguntas de producto son preguntas booleanas disfrazadas.
Existe además una categoría de datos que no debería atravesar nunca la frontera del dispositivo con fines analíticos, con independencia de lo útil que resultara: salud, orientación sexual, creencias, situación financiera detallada, contenido de comunicaciones y cualquier cosa relativa a menores. Aquí la respuesta correcta no es minimizar sino no recoger, y la implementación correcta no es un filtro en el servidor sino un tipo que hace la construcción de ese evento imposible. Si el dato nunca entra en el modelo del evento, ninguna prisa futura podrá colarlo.
La atención se concentra en los eventos de analítica y el escape suele estar en otro sitio: trazas que imprimen objetos completos, informes de fallo con valores adjuntos como contexto, migas de pan que incluyen la carga de una petición, capturas de pantalla automáticas en herramientas de sesión. Todo eso sale del dispositivo y va a un servidor de terceros. La revisión de minimización debe cubrir esos canales con el mismo rigor que los eventos, porque son los que nadie declara.
Identificar, seudonimizar, anonimizar
Los tres términos se usan como sinónimos y designan situaciones jurídicas y técnicas muy distintas. Un dato es identificativo cuando señala a una persona por sí mismo; es seudónimo cuando la señala a través de una tabla de correspondencia que existe en alguna parte; y es anónimo cuando ninguna combinación razonable de esfuerzo y datos auxiliares permite volver a la persona. La consecuencia práctica es dura: casi todo lo que los equipos llaman datos anónimos es en realidad seudónimo, y la normativa lo trata como dato personal con todas sus obligaciones.
El malentendido más extendido merece un párrafo propio porque se repite en casi todas las revisiones. Aplicar una función resumen criptográfica a un correo electrónico o a un identificador de dispositivo no anonimiza nada. La función es determinista, el espacio de correos posibles es enumerable y existen tablas precalculadas: quien tenga el resumen y una lista de correos recupera la correspondencia en segundos. Lo que sí cambia el análisis es añadir una sal secreta que solo vive en tu servidor, rotarla, y no compartir el resultado con nadie; eso produce un seudónimo mejor, no un anónimo.
flowchart TD
a[Dato en el dispositivo] --> b{Sirve para decidir}
b -->|No| c[No se recoge]
b -->|Si| d[Se reduce a tramo o categoria]
d --> e{Permite reidentificar}
e -->|Si| f[Seudonimo con sal rotada]
e -->|No| g[Agregado publicable]
f --> h[Retencion limitada y borrado]En el ecosistema de Apple conviene tener claros los tres identificadores que se cruzan en cualquier discusión. El identificador de anunciantes solo es accesible tras el permiso explícito de seguimiento y, sin él, devuelve un valor nulo constante para todo el mundo; el identificador de proveedor es estable por fabricante y se borra al desinstalar todas sus aplicaciones, y es la elección correcta para telemetría propia; y cualquier identificador que generes tú debe guardarse donde su ciclo de vida sea explicable, lo que descarta el llavero si eso implica sobrevivir a la desinstalación sin que nadie lo espere.
Hay un umbral que conviene interiorizar aunque no se implemente formalmente: la anonimización real solo existe en el agregado y con un tamaño mínimo de grupo. Publicar que el noventa por ciento de las sesiones dura menos de dos minutos es anónimo; publicar el mismo dato segmentado por país, modelo de dispositivo, versión del sistema y día puede dejar celdas con una sola persona dentro. La regla operativa que aplican los equipos serios es no mostrar ninguna celda por debajo de un umbral de recuento, y aplicarla en la herramienta de análisis y no en la buena voluntad de quien consulta.
El manifiesto de privacidad
Desde la introducción de los manifiestos, cada aplicación y cada dependencia que se distribuya debe incluir un archivo PrivacyInfo.xcprivacy que declara, en un formato de lista de propiedades, cuatro cosas: si hay seguimiento, qué dominios se usan para seguimiento, qué categorías de datos se recogen y con qué finalidad, y qué API de motivo requerido se emplean junto al código de justificación correspondiente. Xcode agrega los manifiestos de la aplicación y de todas sus dependencias en un informe único que es el que sustenta las etiquetas de privacidad de la ficha de la tienda.
NSPrivacyTracking -> booleano de seguimiento
NSPrivacyTrackingDomains -> dominios de seguimiento
NSPrivacyCollectedDataTypes -> tipo, vinculacion, seguimiento, finalidad
NSPrivacyAccessedAPITypes -> API de motivo requerido y su codigo
La parte de los motivos requeridos es la que más rechazos de publicación provoca y la que peor se entiende. Apple identificó un conjunto de interfaces que históricamente se usaban para construir huellas digitales de dispositivo —marcas de tiempo de archivos, espacio libre en disco, tiempo de actividad del sistema, valores por omisión de usuario— y ahora exige declarar un código de motivo por cada una que tu código o tus dependencias utilicen. No basta con no usarlas de forma abusiva: hay que declararlas, y el motivo declarado tiene que corresponder al uso real.
Para cada categoría de dato recogido se declaran tres atributos que suelen confundirse entre sí. La vinculación indica si el dato queda asociado a una identidad concreta en tus sistemas; el seguimiento indica si el dato se combina con datos de terceros para publicidad o corretaje de información; y la finalidad es un valor de un conjunto cerrado que incluye analítica, personalización, funcionalidad del producto y varios más. La combinación de estos tres atributos determina literalmente qué etiquetas aparecen en la ficha, y una declaración generosa por precaución produce una ficha alarmante que reduce la conversión de instalación.
Los SDK de una lista publicada por Apple deben incluir además firma criptográfica y manifiesto propio, de modo que la declaración de tu aplicación no dependa de la buena fe de quien integra. La consecuencia práctica es que actualizar una dependencia puede cambiar tus etiquetas de privacidad sin que nadie lo note, así que conviene revisar el informe agregado de privacidad en cada actualización mayor y tratarlo como parte de la revisión, no como un artefacto que se genera al final.
Consentimiento operable
Un consentimiento que no se puede revocar, que no distingue finalidades o que no produce ningún efecto verificable en el comportamiento del código no es consentimiento, es decoración legal. La versión operable tiene cuatro propiedades. Es granular, porque medir errores para arreglarlos y perfilar para publicidad no son la misma petición. Es previo, porque nada se envía antes de la respuesta y el estado por omisión es no recoger. Es revocable desde dentro de la aplicación en un lugar que se encuentre sin buscar. Y es verificable, porque existe una prueba automatizada que falla si algo se envía sin permiso.
import AppTrackingTransparency
// Seguimiento y analitica propia son permisos distintos y se piden distinto
func solicitarSeguimientoSiProcede() async -> Bool {
guard ATTrackingManager.trackingAuthorizationStatus == .notDetermined else {
return ATTrackingManager.trackingAuthorizationStatus == .authorized
}
let estado = await ATTrackingManager.requestTrackingAuthorization()
return estado == .authorized
}
// El invariante que hay que poder demostrar en una prueba
func testNoSeEnviaNadaSinConsentimiento() {
let transporte = TransporteFalso()
let analitica = Analitica(transporte: transporte,
consentimiento: .init(analitica: false))
analitica.registrar(.notaGuardada(origen: .lista, longitud: .corta, adjuntos: 0))
XCTAssertTrue(transporte.enviados.isEmpty)
}
La petición de seguimiento tiene además una dimensión de diseño que decide su resultado. Un diálogo del sistema lanzado en el primer segundo, antes de que nadie entienda qué hace tu aplicación, se rechaza de forma abrumadora y no hay segunda oportunidad porque el sistema solo permite preguntar una vez. Explicar antes, con una pantalla propia y en lenguaje llano, qué se gana concediendo el permiso y qué se pierde negándolo, y preguntar en un momento en el que la persona ya ha obtenido valor, cambia la tasa de aceptación por un factor considerable. Y si tu producto no necesita realmente el seguimiento, no preguntar es una decisión perfectamente defendible.
Queda la parte que casi nadie implementa: el borrado. Si alguien revoca el consentimiento o pide la supresión de sus datos, hace falta un camino técnico real que localice sus registros en el almacén analítico y los elimine, incluidas las copias en herramientas de terceros y los agregados que aún permitan reidentificación. Diseñar el esquema con esa operación en mente desde el principio —un identificador seudónimo consistente que se pueda usar como clave de borrado— es infinitamente más barato que reconstruirlo cuando llega la primera solicitud.
La reacción instintiva de un equipo ante los requisitos de privacidad es tratarlos como un impuesto: trabajo que no mejora el producto y que se paga porque no queda otro remedio. Merece la pena examinar esa intuición porque es empíricamente falsa en casi todos los casos concretos, y el motivo es estructural. Un plan de medición que solo puede recoger lo mínimo se ve obligado a responder primero para qué sirve cada campo, y esa obligación es exactamente la disciplina que la lección anterior proponía y que casi ningún equipo se impone por voluntad propia. Un esquema que debe soportar el borrado por persona necesita una clave coherente y un modelo de datos ordenado, que es lo que se necesita de todos modos para hacer análisis serios. Un sistema que debe declarar cada dependencia y cada acceso a interfaces sensibles acaba con un inventario de dependencias actualizado, que es la base de cualquier práctica de seguridad de la cadena de suministro. La privacidad funciona aquí como funcionó la accesibilidad, o los tipos, o las pruebas: como una restricción externa que fuerza a explicitar decisiones que el equipo estaba tomando de forma implícita y por tanto sin criterio. Y hay una última consideración que no es técnica y que conviene no perder de vista, porque es la que sostiene todo lo demás. Quien instala tu aplicación te está concediendo acceso a un dispositivo que lleva encima todo el día, que sabe dónde duerme y con quién habla. Ese acceso no es un recurso que se explota hasta el límite de lo que la plataforma permite; es una confianza que se administra, y cuya pérdida no se recupera con una actualización. La pregunta que ordena el asunto no es qué me deja recoger la plataforma, sino qué me sentiría cómodo explicando en una pantalla, en lenguaje llano, a la persona cuyos datos son. Si la respuesta incomoda, la instrumentación está mal, no la pregunta.
Minimizar es una decisión campo a campo: tramos en vez de valores, tiempo grueso, banderas en vez de contenido, y categorías prohibidas que se hacen inexpresables en el tipo. Una función resumen no anonimiza: sin sal secreta y rotada sigue siendo seudónimo, y el anonimato real solo existe en agregados con umbral mínimo de grupo. El manifiesto PrivacyInfo.xcprivacy declara seguimiento, dominios, tipos de datos con vinculación y finalidad, y motivos de acceso a API, y se agrega con el de cada dependencia firmada. Y el consentimiento solo cuenta si es granular, previo, revocable, verificado por una prueba y respaldado por un borrado que funciona.
- Intercepta durante una sesión todo el tráfico saliente de telemetría y haz un inventario real de campos, comparándolo con lo que creías estar enviando.
- Toma los cinco eventos con más propiedades y aplica el catálogo de sustituciones hasta que ningún campo contenga contenido ni valores exactos.
- Escribe el manifiesto de privacidad de tu aplicación, genera el informe agregado con las dependencias y compáralo con las etiquetas de tu ficha actual.
- Añade una prueba que falle si se envía cualquier evento con el consentimiento desactivado y otra que verifique que la revocación detiene el envío en caliente.
- Diseña el procedimiento de borrado por persona de principio a fin y ejecútalo sobre un identificador de prueba, incluyendo las herramientas de terceros.