Proxy seguro de telemetría para aplicaciones móviles
Las aplicaciones móviles se distribuyen a dispositivos que usted no controla. Todo lo compilado en un paquete React Native, una aplicación Swift, una aplicación Kotlin o un binario Flutter puede eventualmente inspeccionarse. No envíe una clave Telemetry API en la aplicación, un valor de configuración remota entregado a la aplicación ni un archivo de entorno legible por el cliente.
En su lugar, envíe un pequeño evento a un punto final autenticado propiedad de su aplicación. Ese punto final valida un contrato fijo, agrega un contexto de servidor confiable y reenvía el evento con una clave del lado del servidor.
Diagrama de un proxy móvil: la aplicación envía eventos a su API, que autentica, valida los eventos permitidos y limita la frecuencia antes de enviarlos a Telemetry. La aplicación móvil no recibe la clave de ingesta de Telemetry.
Cuando este patrón encaja
Utilice un proxy móvil para hitos de productos, mediciones de rendimiento limitadas, resultados de sincronización, categorías de errores controladas y señales de calidad de lanzamiento. Los ejemplos incluyen:
mobile_sync_completeddespués de que una sincronización local con el servidor alcanza un resultado de terminal.mobile_screen_readypara un pequeño conjunto de pantallas con nombre y una duración medida.mobile_purchase_flow_completeddespués de que el servidor confirme el resultado duradero.mobile_api_request_completedpara obtener resultados de dependencia categorizados y muestreados.
Esto no reemplaza la simbolización de fallos, los registros de dispositivos, los seguimientos distribuidos ni un libro de auditoría exacto. La entrega móvil se ve afectada por la conectividad, los límites de ejecución en segundo plano, la terminación de la aplicación, el consentimiento y la calidad del reloj del cliente. Mantenga diagnósticos especializados en sus sistemas especializados y envíe solo el resultado necesario para el análisis SQL.
Definir el contrato del cliente.
El cliente debe elegir de una lista versionada de nombres y campos de eventos. No debe elegir un nombre de tabla Telemetry, enviar bolsas de propiedades arbitrarias ni reenviar mensajes de excepción.
{
"event_name": "mobile_sync_completed",
"event_version": 1,
"event_id": "0196f37e-6e83-7b75-9d04-6b8283b35f74",
"occurred_at": "2026-07-30T18:42:11.150Z",
"status": "success",
"duration_ms": 842,
"item_count": 12,
"network_type": "wifi"
}
Mantenga las dimensiones acotadas. Prefiera un screen_name controlado a una ruta sin formato, una enumeración error_type a una cadena de excepción y un network_type aproximado a identificadores de red. No incluya tokens de acceso, identificadores de publicidad de dispositivos, datos de contacto, contenidos de mensajes, rutas de archivos, datos del portapapeles, texto de búsqueda de formato libre ni URL sin formato.
event_id admite reintentos de deduplicación. occurred_at registra la observación del cliente, pero el proxy también debe agregar una marca de tiempo recibida por el servidor. Utilice la marca de tiempo del servidor para monitorear la actualización y la ingesta porque el reloj de un dispositivo puede estar incorrecto.
Implementar el punto final propiedad de la aplicación
El siguiente boceto TypeScript muestra el límite. Adapte la autenticación y la limitación de velocidad al marco que ya utiliza su API.
const allowedEvents = {
mobile_sync_completed: {
statuses: new Set(["success", "failed", "cancelled"]),
maximumDurationMs: 300_000,
maximumItemCount: 10_000,
},
} as const;
export async function postMobileTelemetry(request: Request) {
const actor = await authenticateApplicationRequest(request);
if (!actor) return new Response("unauthorized", { status: 401 });
await enforceRateLimit({
accountId: actor.accountId,
deviceSessionId: actor.deviceSessionId,
});
const body = await readBoundedJson(request, { maximumBytes: 4096 });
const policy = allowedEvents[body.event_name as keyof typeof allowedEvents];
if (
!policy ||
body.event_version !== 1 ||
!isUuid(body.event_id) ||
!policy.statuses.has(body.status) ||
!isIntegerInRange(body.duration_ms, 0, policy.maximumDurationMs) ||
!isIntegerInRange(body.item_count, 0, policy.maximumItemCount)
) {
return new Response("invalid event", { status: 422 });
}
await telemetry.log("mobile_sync_completed", {
event_id: body.event_id,
event_version: 1,
occurred_at: parseBoundedClientTimestamp(body.occurred_at),
received_at: new Date().toISOString(),
status: body.status,
duration_ms: body.duration_ms,
item_count: body.item_count,
network_type: normalizeNetworkType(body.network_type),
account_id: actor.accountId,
app_platform: actor.platform,
app_version: actor.appVersion,
environment: process.env.APP_ENV ?? "development",
});
return new Response(null, { status: 202 });
}
El API decide el nombre del evento Telemetry. Deriva la identidad, la plataforma, el entorno y cualquier contexto de autorización del estado del servidor confiable en lugar de aceptar esos valores del cliente. Si el punto final admite más de un evento, asigne a cada evento un esquema y un dispositivo de prueba independientes.
Aplicar cuatro controles del lado del servidor
autenticar
Requiere la misma sesión de aplicación firmada o credencial de instalación utilizada por el resto de su API. CORS no es un límite de seguridad móvil y un encabezado personalizado por sí solo no prueba quién envió una solicitud.
Lista de permitidos
Rechace nombres de eventos, campos, valores de enumeración, cadenas de gran tamaño, números no válidos, marcas de tiempo futuras y cuerpos que superen un límite de tamaño pequeño desconocidos. La lista de permitidos es tanto un control de privacidad como un control de cardinalidad.
Límite de tarifa
Límite por cuenta autenticada y un identificador de sesión o instalación adecuado. También imponer un techo global. Devuelve un error de aplicación normal sin reintentar indefinidamente cuando un cliente excede el límite.
Agregar contexto confiable
El proxy debe agregar el identificador de la cuenta, la hora de recepción del servidor, el entorno y la versión verificada de la aplicación cuando esos valores estén disponibles. Si un identificador de cuenta es confidencial en su contexto, pseudónimo consistentemente antes de recopilarlo y documente quién puede revertir la asignación.
Elija el comportamiento de entrega móvil
Para React Native y Flutter, utilice el cliente de plataforma HTTP que ya es responsable de las solicitudes API autenticadas. Para iOS y Android nativos, use la misma pila URLSession o HTTP que usa la aplicación. El contrato de punto final y de evento debe permanecer idéntico en todas las plataformas.
Mantenga una pequeña cola limitada cuando la entrega fuera de línea sea importante. Limite tanto el recuento como la antigüedad de los elementos, descarte los eventos que hayan excedido la ventana documentada y utilice un retroceso exponencial con fluctuación. No permita que los reintentos de análisis retrasen la acción del producto. Conserve el mismo event_id en todos los intentos para que el servidor pueda deduplicar.
Algunos resultados son mejor emitidos por el servidor. Una compra, un cambio de suscripción, una decisión de política de acceso o una importación completa adquieren autoridad solo después de que el backend la confirma. Deje que el servidor emita esos resultados directamente en lugar de confiar en la afirmación del cliente.
Consulta y verifica el resultado.
Envíe un éxito sintético, un error, una cancelación, una carga útil no válida, una solicitud no autenticada, una ráfaga de velocidad limitada, un reintento sin conexión y un event_id duplicado. Luego inspeccione una muestra limitada:
SELECT
received_at,
event_id,
app_platform,
app_version,
status,
duration_ms,
item_count
FROM mobile_sync_completed
ORDER BY received_at DESC
LIMIT 50;
Antes del lanzamiento, verifique:
- El binario de la aplicación enviada y el paquete JavaScript no contienen ninguna clave Telemetry API.
- Los eventos y campos desconocidos se rechazan en lugar de reenviarse silenciosamente.
- Faltan texto de excepción sin formato, URL, entradas de usuario, credenciales e identificadores de dispositivos.
- Los reintentos mantienen un
event_idy la entrega duplicada no infla el recuento de resultados. - Los paneles muestran el recuento de muestras junto a tasas y percentiles.
- El comportamiento de consentimiento, retención, eliminación y eliminación de cuentas coincide con la política de la aplicación.
Modos de falla para planificar
Trate el proxy como observabilidad del mejor esfuerzo, a menos que el evento sea parte de un flujo de trabajo empresarial duradero diseñado por separado. Normalmente se debe registrar y limitar un tiempo de espera Telemetry sin que falle la acción del producto móvil. Supervise la tasa de rechazo de proxy, los errores de reenvío, la antigüedad de la cola y la actualidad de los eventos para que una interrupción silenciosa de la instrumentación no parezca una caída en el uso del producto.
No acepte automáticamente eventos de cliente arbitrarios para "facilitar la depuración". Eso convierte el punto final en una superficie de recopilación de datos no auditada. Agregue un nuevo campo o evento versionado solo después de revisar su propósito, tipo, cardinalidad, clasificación de privacidad y requisitos de eliminación.
Referencias primarias
- Guía móvil de OWASP sobre claves API codificadas y patrones de proxy del lado del servidor
- Flutter networking: envía datos a internet
- Entrega de eventos Telemetry, idempotencia y límites de reintento
- Guía de eliminación de datos sensibles confidenciales Telemetry
- Guía de proxy del navegador para el modelo de entrega web relacionado