Volver al Centro de conocimiento
Huella digital

Detección de Android WebView con CreepJS: límites

Separa las señales de Android WebView entre HTTP, JavaScript, workers y puentes de la app; descubre qué comprueba CreepJS y qué no demuestra.

BotBrowser Team

Documentación

Quieres la documentación estructurada de Huella digital?

Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.

BotBrowser soporta identidades Android WebView declaradas para pruebas autorizadas de compatibilidad; no puede decidir cómo clasifica un servicio anti-bot privado un navegador integrado. La configuración WebView usa una plantilla explícita de --user-agent con wv; no elige un valor de X-Requested-With específico de una app. La pregunta útil es más concreta: ¿qué capa proporcionó cada valor y qué comparaciones puede hacer realmente la página?

TL;DR

  • ; wv en un User-Agent es una pista de la familia WebView, no una prueba de una app concreta.
  • X-Requested-With y window.android dependen de la app anfitriona; ninguno es universal.
  • El módulo navigator de CreepJS compara valores seleccionados de la página con los del worker y consulta User-Agent Client Hints.
  • Una discrepancia demuestra que dos observaciones difieren; no identifica la causa ni establece un veredicto de automatización.

Contenido

Capas de señal: cabeceras HTTP, JavaScript de página, valores del worker y puentes opcionales de la app
La procedencia de las señales separa las observaciones del navegador de la identidad de la app y de las conclusiones sobre automatización.

Ver el vídeo

Este vídeo en inglés separa las cabeceras de solicitud, el JavaScript de la página, los valores del worker y los puentes opcionales de la app, y muestra qué puede y qué no puede establecer una ejecución controlada del navegador. No autentica una app anfitriona ni reproduce un dispositivo Android WebView.

La app anfitriona no es la superficie del navegador

Android WebView es un componente de navegador que una aplicación Android aloja. La página puede observar propiedades del navegador y de las solicitudes, mientras la app anfitriona puede elegir un User-Agent, añadir cabeceras o exponer un puente JavaScript. Son decisiones distintas y con ámbitos distintos, y deben registrarse por separado.

La documentación de Android WebView de BotBrowser explica cómo sus ajustes de identidad declarada presentan un formato de User-Agent con wv y cabeceras opcionales de la app. La pregunta de detección es más concreta: qué puede inferir un detector de cada superficie y qué queda sin saber. Para configurar y alinear la identidad, consulta la guía de Android WebView; para ver cómo CreepJS agrupa lecturas independientes, consulta los grupos de señales y límites de observación de CreepJS.

De dónde procede cada señal

SeñalCapa observableQué puede respaldarQué no demuestra por sí sola
User-Agent con ; wvSolicitud HTTP y navigator.userAgentSe presentó un UA de estilo WebViewQué app lo integra ni si el valor está actualizado
Sec-CH-UA y navigator.userAgentDataCabeceras y JavaScript, cuando están disponiblesMarca y plataforma expuestas por ese contextoQue todos los WebView Android expongan las mismas pistas
navigator.platformJavaScript de la páginaUna observación de familia de plataformaModelo del dispositivo o app anfitriona
Valores WorkerNavigatorJavaScript del workerSi ciertos valores coinciden entre página y workerSi alguno coincide con el hardware físico
X-Requested-WithSolicitud HTTP, cuando la app lo envíaQue la app pudo añadir una cabecera parecida a un paqueteUn marcador universal de WebView o identidad de app fiable
window.android u otro puenteJavaScript, solo si se inyectóQue el host expuso una interfaz con nombreQue todas las apps lo expongan o que sea seguro invocarlo

Android expone el User-Agent mediante WebSettings, y el host puede configurarlo. Android también documenta addJavascriptInterface como una operación explícita de la aplicación; el nombre del puente lo elige la app. Por eso, la ausencia de un puente no descarta WebView y un nombre presente no debe tratarse como un identificador autenticado del paquete.

Las cabeceras y JavaScript deben ocupar filas distintas en el registro. El servidor ve una cabecera antes de que se ejecute el código de la página; el código ve propiedades en su propio contexto. Una redirección, un service worker, un wrapper de app o un cambio de versión puede afectar a una capa sin cambiar las demás.

Las familias de plataforma son amplias

El helper getReportedPlatform() de CreepJS reduce varios nombres de sistema a familias generales. En la ruta del User-Agent, android, linux y cros se asignan a la misma familia Linux. La ruta de navigator.platform también agrupa android, arm y linux bajo esa familia. Sirve para comparar coherencia, pero pierde distinciones: una coincidencia de familia Android no identifica WebView ni nombra la app anfitriona.

El módulo navigator realiza además una comprobación separada de subcadenas y acepta android entre sus etiquetas reconocibles. Esa comprobación determina si la cadena parece conocida en esta revisión, no si una app concreta la generó. Guarda el campo original, la familia normalizada y el resultado de cada comprobación en columnas distintas. Un informe que solo conserve Android = true pierde si la fuente fue el UA, navigator.platform o una cabecera de solicitud.

La distinción resulta útil al comparar WebView, Chrome Custom Tabs y Chrome completo. Los tres pueden ejecutarse en Android y presentar combinaciones diferentes de identidad del navegador y comportamiento de la app anfitriona. Coincidir en una familia amplia no demuestra que sus marcas, cabeceras o puentes JavaScript sean iguales.

Las comprobaciones navigator tienen disparadores distintos

El módulo fijado no trata todos los valores inusuales como el mismo hallazgo. Si existe el objeto global chrome, comprueba si navigator.userAgent contiene navigator.appVersion; también busca espacios iniciales, finales o repetidos y aplica una comprobación de texto gibberish definida por el proyecto. Son observaciones sobre la calidad del User-Agent. Después compara userAgent con el valor del worker y marca esa diferencia entre contextos por separado.

Para deviceMemory, la fuente reconoce 0.25, 0.5, 1, 2, 4, 8 y 16, además de 32. Si existe performance.memory.jsHeapSizeLimit, convierte el límite a gigabytes y comprueba si supera el valor de memoria declarado. También compara deviceMemory con el worker. hardwareConcurrency tiene su propia comparación de igualdad entre página y worker. Son condiciones distintas, no una puntuación Android única.

La ruta de idioma compara los dos primeros caracteres de navigator.language con los de la primera entrada de navigator.languages. Cuando ambos campos de página tienen un valor verdadero, el código devuelve el resultado de idioma formateado tras esa comparación, coincidan o no los prefijos. Las comparaciones de idioma del worker solo ocurren fuera de esa rama. El tratamiento de plugins es distinto: combina navigator.plugins con navigator.mimeTypes y comprueba la coherencia de ese par. Un registro útil de compatibilidad nombra la propiedad y el disparador exactos en vez de convertir comprobaciones no relacionadas en una etiqueta WebView.

Client Hints depende de la disponibilidad

El módulo navigator de CreepJS no devuelve un resultado de User-Agent Client Hints cuando falta navigator.userAgentData o getHighEntropyValues(). Cuando ambos existen, solicita platform, platformVersion, architecture, bitness, model y uaFullVersion; después combina esos campos con brands y mobile de baja entropía.

Antes de presentar la lista, el código elimina marcas cuyo nombre contiene Not y quita Chromium de la lista mostrada cuando hay varias marcas. Esta transformación forma parte del formato de salida de la biblioteca. No demuestra que Chromium estuviera ausente del navegador ni que todas las plataformas devuelvan los mismos campos de alta entropía.

Distingue tres resultados: la API no existe, se omite un valor solicitado o la API devuelve un valor. Las fuentes citadas establecen que Client Hints es condicional, pero no atribuyen una causa a un valor omitido. Registra el estado observado antes de comparar contextos, para no confundir una pista ausente con una pista incoherente.

Qué comprueba el código fijado de CreepJS

La revisión pública de CreepJS citada aquí está fijada al commit 10aa6724cd33a1015db1574211890518cd04f0cc. Su módulo navigator acepta Android entre varias subcadenas reconocibles de navigator.platform. También compara la familia de sistema inferida del User-Agent con la plataforma informada. Es una comprobación de coherencia, no una regla de clasificación específica para WebView.

El mismo módulo compara ciertos valores de navigator en la página con valores obtenidos de un worker. El código incluye comparaciones de platform, userAgent, deviceMemory, hardwareConcurrency, language y languages; las comparaciones de idioma con el worker quedan fuera de la rama en que ambos campos de idioma de página tienen un valor verdadero. Si la API está disponible, su ruta de User-Agent Client Hints solicita campos como platform, platformVersion, architecture, bitness, model y uaFullVersion. Son detalles de implementación del módulo navigator fijado, no una afirmación de que todos los WebView Android ofrezcan cada campo.

El módulo headless separado informa grupos de condiciones visibles desde el navegador. Sus grupos e inputs no deben reducirse a un veredicto sobre Android WebView. El análisis de CreepJS sobre headless explica por qué esos grupos contestan preguntas distintas.

El código respalda una afirmación acotada: los módulos inspeccionados de CreepJS pueden comparar superficies seleccionadas e informar determinadas condiciones relacionadas con headless. Esas comprobaciones no autentican el paquete Android que creó el WebView ni revelan la regla de decisión de un tercero.

Separa la evidencia de WebView de otras conclusiones

Usa cuatro etiquetas en un informe de compatibilidad en vez de un veredicto binario:

Etiqueta del informeEvidencia mínimaInterpretación prudente
Contexto de familia AndroidObservaciones coherentes de plataforma Android y UAEl contexto visible informa de la familia Android
Contexto de estilo WebViewUA o marca de estilo WebView, registrando por separado petición y páginaEl contexto presenta una o más propiedades asociadas a WebView
Diferencia entre contextosDifiere una comparación identificada entre página/worker o cabecera/JavaScriptEsas observaciones no coinciden en esta ejecución
Conclusión de automatizaciónEvidencia independiente y regla de decisión documentadaUna sola señal de página no basta

Esta distinción importa al revisar la alineación de marcas del navegador. Marca, plataforma y comportamiento de la app integrada están relacionados, pero no son campos intercambiables. Un navigator.platform distinto no explica una cabecera distinta salvo que el mismo registro vincule ambas observaciones y conserve su procedencia.

Una discrepancia solo demuestra una diferencia

En esta fuente, una diferencia entre página y worker demuestra que los valores comparados difieren en esos contextos observados. No establece si el valor de página, el del worker o ambos son los esperados para una versión concreta. Tampoco identifica la causa. El código describe su propia comparación, pero no autentica la app, el sistema operativo, el proveedor del navegador ni la política de un tercero.

Mantén separadas cuatro afirmaciones. Primero, registra el valor observado y su capa. Segundo, anota si el campo existe y puede leerse. Tercero, indica la comparación exacta que difiere, como navigator.platform frente a platform del worker. Cuarto, limita la consecuencia a la prueba de compatibilidad propia. Solo una regla de decisión independiente y documentada justifica una conclusión más amplia sobre automatización.

No extiendas en silencio un hallazgo de un campo al resto de la sesión. Una cabecera X-Requested-With distinta no informa por sí sola sobre navigator.userAgentData; una diferencia de worker tampoco describe un puente de la app. Así se conserva el valor diagnóstico sin convertir la medida en una afirmación de identidad que no respalda.

Los resultados de fuentes tipográficas son otra superficie: el análisis de tablas de fuentes de CreepJS muestra por qué un grupo tipográfico detectado no identifica la app que aloja un WebView.

La consecuencia para la privacidad también es concreta. window.android puede exponer métodos de la aplicación y Android advierte que los puentes JavaScript amplían la frontera de seguridad. Una revisión debe anotar si existe un puente sin invocar métodos ajenos a la prueba ni recopilar datos de la app innecesarios.

Una comparación controlada en apps propias

En una app propia, compara tres contextos con la misma versión de Android y la misma compilación de la app: el WebView de la app, Chrome Custom Tab si el producto lo usa y Chrome completo. Conserva el origen y la acción de prueba. La tabla es un plan de captura, no el informe de una ejecución:

Punto de capturaRegistrarMantener separado
SolicitudUser-Agent, cabeceras Client Hints y X-Requested-With si existeNavegación inicial frente a solicitudes posteriores
Páginanavigator.userAgent, navigator.platform y campos disponibles de userAgentDataAusente, denegado y vacío son estados distintos
WorkerCampos correspondientes de WorkerNavigatorMomento de creación y origen del worker
App anfitrionaSi se registra un puente de forma intencional y su nombre documentadoNo invocar métodos solo para identificar el contenedor

Versiona juntos Android, el proveedor WebView, la compilación de la app y la página de prueba. Comparar proveedores distintos puede hacer que una actualización legítima parezca una incoherencia. Para cada celda distinta, registra su capa y si el campo estaba ausente, no disponible o tenía otro valor; no conviertas esos estados en una única puntuación.

Conserva el contexto y limita la recopilación

Usa la misma acción de una página propia y el mismo origen, y mantén comparable el momento de navegación. Registra la versión de Android, WebView, app y navegador, el idioma, el estado de permisos y si el contexto es WebView, una pestaña de Chrome integrada o Chrome completo. Esos datos explican qué se comparó; no son un veredicto. Si una actualización cambia un valor, conserva ambas versiones en vez de llamarlo una mentira.

La cabecera X-Requested-With, que puede parecerse al nombre de un paquete, solo debe guardarse cuando la pregunta de compatibilidad la necesite y exista autorización. En informes compartidos, basta con marcar su presencia o redactar el valor. Un puente JavaScript puede exponer métodos; registra si la app lo añade intencionadamente y su nombre documentado, pero no llames métodos ajenos a la prueba ni serialices datos de la app. La advertencia de Android sobre addJavascriptInterface lo convierte también en un límite de seguridad.

Vincula cada comparación a su fuente: captura de solicitud para cabeceras, captura de página para navigator y captura de worker para sus valores. Un registro reducido puede guardar el nombre del campo, su estado, las versiones y la pareja comparada sin incluir contenido del usuario. Es suficiente para revisar compatibilidad propia y reduce la recopilación accidental de identificadores y datos de la aplicación.

Mantén comparable el momento de la solicitud

Captura por separado la navegación principal y las solicitudes posteriores de subrecursos. La app puede adjuntar una cabecera solo a algunas peticiones; por eso, una cabecera de una imagen no debe compararse con el JavaScript del documento final como si ambos datos fueran una sola observación. Vincula cada solicitud con su navegación, origen, compilación de la app, versión del navegador y permisos. Si hay redirecciones, registra cada salto en vez de tratar la primera y la última URL como una transacción. La comparación describe ese contexto y ese momento, no todos los dispositivos con la misma versión principal de Android.

Preguntas frecuentes

¿; wv demuestra que la petición procede de Android WebView?

No. Es una cadena reconocible del User-Agent que la aplicación puede configurar. Aporta evidencia sobre el valor presentado, no demuestra qué paquete integra el navegador ni el origen de la petición.

¿CreepJS identifica la app Android que aloja la página?

Las comprobaciones navigator citadas comparan valores visibles y valores del worker. No autentican el paquete anfitrión. Un puente o una cabecera inyectados por la app dependen de la aplicación; no son una señal universal de identidad CreepJS.

¿Una diferencia página/worker significa que el navegador es automatizado?

No. Significa que los valores capturados difieren. Una actualización, el soporte de una API, el momento del contexto o la configuración de la app también pueden explicarlo. Conserva los campos y el entorno exactos antes de ampliar la conclusión.

Fuentes

Conclusión práctica

Trata la identificación de WebView como una pregunta sobre procedencia, no como una prueba de un token. Registra por separado cabeceras, propiedades de página, comparaciones de worker y puentes opcionales. En QA de apps propias, compara contextos equivalentes y conserva los valores ausentes: una discrepancia es un hallazgo de compatibilidad útil, pero no demuestra identidad del dispositivo ni una decisión anti-automatización de terceros.

#CreepJS#Android WebView#señales del navegador

Este artículo forma parte de la guía: ¿Qué es la huella digital del navegador?

Lleva BotBrowser de la investigación a producción

Usa estas guías para entender el modelo y después avanzar hacia validación multiplataforma, contextos aislados y despliegue de navegador preparado para escalar.