Fuentes de CreepJS: 51 nombres y 14 retornos posibles
CreepJS consulta 51 nombres de fuentes para inferir una versión de Windows o macOS. Ejecutar sus tablas muestra que cada respuesta exige un conjunto base completo.
BotBrowser Team
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 puede reproducir un perfil declarado contra una comprobación de fuentes propia de la página y guardar un registro acotado de las familias que la página encontró, pero no puede hacer que un conjunto de fuentes instaladas describa otro sistema operativo. CreepJS lee una lista fija de nombres de fuentes y convierte los que una página puede detectar en una versión de Windows o macOS. Ejecutar sus tablas publicadas muestra que la búsqueda es más estricta de lo que sugiere la forma de la tabla: cada versión que puede nombrar exige un conjunto base completo de fuentes, así que una familia suelta no decide nada.
Los tres nombres de fuentes Android de esta fuente no identifican qué contexto de inserción los expuso. Los límites de las señales de CreepJS en Android WebView distingue los valores visibles para la página de las señales de solicitud y del puente opcional de la app.
Ver el vídeo
Este vídeo en inglés trata la búsqueda publicada en las tablas de fuentes. El código enumera 13 versiones con nombre para Windows y macOS; el decimocuarto retorno posible es undefined cuando no coincide ningún conjunto base completo. El audio está en inglés. El registro reproducible usa conjuntos sintéticos de nombres de fuentes y no afirma medir fuentes en un navegador activo.
Resumen
- El módulo publicado declara 51 nombres de fuentes en cinco tablas: 14 de Windows, 18 de macOS, 9 de aplicaciones de escritorio, 7 de Linux y 3 de Android.
- Los 51 nombres ya son únicos: ningún nombre aparece en dos tablas, así que no hay nada que colapsar antes de la comprobación.
- La tabla de Windows reparte sus 14 nombres en cinco grupos de versión, y el grupo más pequeño contiene un solo nombre.
- La tabla de macOS reparte sus 18 nombres en ocho grupos, y dos de ellos contienen un solo nombre.
- Un grupo de un solo nombre es necesario pero nunca suficiente: el mapa de Windows solo tiene entradas para claves que contienen el par completo de Windows 7, así que
Segoe Fluent Iconssolo no devuelve nada. - CreepJS usa dos rutas de detección,
document.fonts.checky una carga local de fuentes, y une ambas respuestas en una lista ordenada. - Ejecutar la búsqueda da 5 respuestas de Windows, 8 de macOS y una respuesta ausente, así que puede devolver 14 valores en total.
Contenido
- Ver el vídeo
- Qué declaran las tablas de fuentes
- Por qué un nombre nunca basta
- Qué añaden las dos rutas de detección
- Dónde termina la inferencia
- Registrar una lectura de fuentes
Qué declaran las tablas de fuentes
El módulo de fuentes es un archivo de datos con algo de lógica encima. La sonda evalúa cada tabla como datos en lugar de escanear su texto, porque las etiquetas de versión son claves entrecomilladas que un escaneo de texto contaría como nombres:
source -> https://raw.githubusercontent.com/abrahamjuliot/creepjs/master/src/fonts/index.ts
source_bytes -> 12847 sha256=ce2bcefd13e41cef79bfdf63634ad17ca7f641ee2e88c0ef720c2d38938dbf11
table:WindowsFonts -> names=14 shape=buckets
table:MacOSFonts -> names=18 shape=buckets
table:DesktopAppFonts -> names=9 shape=buckets
table:LINUX_FONTS -> names=7 shape=list
table:ANDROID_FONTS -> names=3 shape=list
fontlist -> length=51 distinct=51 duplicated=0
overlap:windows_macos -> 0
overlap:linux_android -> 0
Cincuenta y un nombres, sin duplicados. Ese es todo el conjunto de detección de la mitad de fuentes de la biblioteca. Una página que ejecuta el módulo pregunta por 51 familias, y cada conclusión de plataforma que el módulo puede sacar viene de cuáles de esas 51 encontró.
La misma fuente pública también alimenta los grupos de señales de CreepJS y cómo los combina, donde la lista de fuentes es un grupo entre varios, y el ciclo de semilla de canvas de CreepJS, que lee el generador del módulo de canvas.
Las cinco tablas no son ortografías alternativas de una lista. Los siete nombres de Linux son familias que trae una imagen de Ubuntu o Chrome OS, y los tres de Android son un conjunto móvil que se solapa con Chrome OS y no con un escritorio. Cada tabla aporta nombres que las otras no tienen, y el módulo las une una sola vez en una lista alfabética que la página comprueba.
Windows y macOS están casi equilibrados: 14 nombres frente a 18. Esa cercanía engaña por otro motivo, porque las dos búsquedas leen sus tablas con reglas distintas, y la regla más estricta es la que decide si aparece una respuesta.
Por qué un nombre nunca basta
Las tablas no son listas planas. Cada versión es un grupo, y la búsqueda pregunta si está presente algún nombre del grupo:
bucketsizes:WindowsFonts -> 7=2 8=4 10=4 11=1 8.1=3
bucketsizes:MacOSFonts -> 12=3 13=4 10.9=2 10.10=2 10.11=1 10.12=3 10.13-10.14=1 10.15-11=2
hashkeys:windows -> 11 macos=8
answers:windows -> 5 11,10,8.1,8,7
answers:macos -> 8 Ventura,Monterey,10.15-11,10.13-10.14,Sierra,El Capitan,Yosemite,Mavericks
answers:possible_returns -> 14
El grupo de Windows 11 contiene un solo nombre, Segoe Fluent Icons. El grupo de macOS 10.11 contiene uno, y también el de 10.13-10.14. Leer solo los grupos sugiere que encontrar una de esas fuentes fija la versión. Ejecutar la búsqueda dice otra cosa.
Cada clave de versión se calcula, se une en una cadena ordenada y se busca en un mapa fijo. Todas las entradas de ese mapa contienen la etiqueta Windows 7, porque la prueba de Windows 7 es la única que exige un conjunto completo en lugar de cualquier nombre suelto. La unión ordenada coloca 7 donde cae su orden alfabético, así que la posición de la etiqueta varía aunque su presencia no. Un conjunto que tiene Segoe Fluent Icons pero no los dos nombres de Windows 7 no coincide con ninguna clave:
replay:windows_11_bucket_only -> undefined (input=1)
replay:windows_7_both_names -> Windows 7 (input=2)
replay:windows_7_one_name -> undefined (input=1)
sweep:windows:undefined -> 5 subsets 11 10 11+10 11+8.1 11+8
sweep:windows:Windows 11 -> 1 subsets 11+10+8.1+8
El barrido anterior son todos los subconjuntos de los cuatro grupos incrementales añadidos al par de Windows 7. Cinco de dieciséis combinaciones no devuelven nada. La única combinación que devuelve Windows 11 es el conjunto completo de los cinco grupos. Quitar un grupo entero y quitar una familia de él son sucesos distintos, y la sonda los separa:
drop:Segoe Fluent Icons -> Windows 10
drop:Segoe MDL2 Assets -> Windows 11
drop:Segoe UI Emoji -> Windows 11
drop:Cambria Math -> macOS Ventura
Un grupo de un solo nombre no tiene margen: perder Segoe Fluent Icons cuesta la respuesta de Windows 11. Un grupo de varios nombres sí lo tiene, porque sigue siendo verdadero mientras exista cualquiera de sus nombres: Segoe MDL2 Assets está en el grupo de cuatro nombres de Windows 10 y Segoe UI Emoji en el grupo de tres de 8.1, así que perder cualquiera devuelve Windows 11 igual. Perder un nombre de Windows 7 es el caso más afilado: la búsqueda de Windows no devuelve nada y entra el respaldo de macOS. En una máquina que además tiene las familias de macOS la respuesta pasa a macOS Ventura; en una con solo fuentes de Windows queda en undefined. En ambos casos la respuesta de Windows desaparece por una sola familia ausente.
El lado de macOS es todavía más estricto. Las ocho claves del mapa empiezan por el par 10.9, y el mapa no tiene entrada para un conjunto con grupos más nuevos pero sin él:
sweep:macos:macOS Mavericks -> 1 subsets base:10.9
sweep:macos:macOS Ventura -> 1 subsets 13+12+10.15-11+10.13-10.14+10.12+10.11+10.10
sweep:macos:undefined -> 120 subsets
Ciento veinte de los 128 subconjuntos no devuelven nada. Cada una de las ocho respuestas nombradas corresponde a exactamente un subconjunto, y la respuesta es el grupo más nuevo presente. Una máquina que solo informa fuentes de la época de Ventura y le faltan Helvetica Neue y Geneva no obtiene ninguna respuesta de macOS, aunque se encontraron cuatro nombres del grupo de Ventura.
Así que los grupos de un solo nombre son reales pero estrechos. Segoe Fluent Icons distingue Windows 11 de Windows 10 solo después de que los dos nombres de Windows 7 y otros tres grupos ya hayan coincidido. La asimetría que merece registrarse es la que hay entre la forma de la tabla y la forma de la búsqueda: la tabla parece un conjunto de pruebas OR independientes, y el mapa se comporta como una escalera que solo arranca cuando el conjunto base está completo.
Esa escalera tiene una segunda consecuencia que un recuento de grupos esconde. Como cada clave es la unión ordenada de todas las entradas verdaderas, un conjunto con un grupo extra que el autor no previó produce una cadena sin entrada en el mapa. La búsqueda no recurre a la coincidencia más cercana; devuelve nada. Las tablas publicadas están cerradas bajo sus propios nombres, así que todo conjunto real de Windows 11 con los 14 nombres, incluidas las cuatro familias de Windows 8, sigue devolviendo Windows 11; el riesgo está en un cambio de tabla que añada un grupo sin añadir su clave.
La tabla de aplicaciones de escritorio se comprueba con otra regla más, y la diferencia conviene decirla sin rodeos porque es el único punto donde se exige una coincidencia completa a propósito. getDesktopApps compara el conjunto de fuentes de cada aplicación con la lista encontrada y solo devuelve la clave cuando están todos los nombres. Cuatro aplicaciones producen por tanto como mucho cuatro resultados, y una coincidencia parcial no produce ninguno.
Qué añaden las dos rutas de detección
El módulo no se apoya en una sola forma de preguntar si una fuente está presente:
detection_paths -> 2 document.fonts.check and local font load
union -> fontsChecked then fontsLoaded, deduplicated
La ruta de document.fonts.check pregunta directamente al conjunto de fuentes. Informa si una familia está disponible para el documento, y el módulo recorre los 51 nombres con él. La ruta de local font load construye un objeto por nombre y espera los resultados de carga, quedándose con los que se cumplen.
Existen dos rutas porque pueden discrepar. Un navegador puede informar que una fuente está disponible por una API y fallar al cargarla por la otra, y una página que solo comprobara una se perdería la discrepancia. La unión de las dos respuestas es la lista final del módulo, ordenada y sin duplicados, así que el resultado es estable frente al orden en que volvieron las dos rutas.
Un informe que guarda las dos listas por separado también puede decir si una API es sistemáticamente más débil en una máquina. Si la ruta de consulta no encuentra nada mientras la de carga encuentra nombres, lo más probable es que se disparara la comprobación, y eso es una observación distinta de una máquina que de verdad no tiene fuentes.
Antes de recorrer la lista, el módulo comprueba un nombre de familia deliberadamente aleatorio. Cuando esa comprobación informa que el nombre aleatorio está disponible, el módulo se salta la consulta por nombre y se apoya en la ruta de carga. Esa comprobación evita confiar en un conjunto que dice contener todos los nombres.
Esa comprobación se pasa por alto con facilidad al leer el informe, porque una consulta omitida y una consulta fallida dejan la lista comprobada vacía. Los dos estados solo se distinguen si el informe dice si la prueba del nombre aleatorio devolvió verdadero. Quien vea una lista comprobada vacía y una lista cargada llena debería suponer que se disparó la comprobación, no que todas las familias fallaron la prueba de disponibilidad.
Un nombre unido por deduplicación se ve igual que uno encontrado una vez, así que el informe debe guardar las dos listas por separado. La búsqueda es independiente del orden por otra razón: las pruebas de grupo usan pertenencia y una comparación de longitud, y la clave se construye ordenando las etiquetas de grupo, no la lista de fuentes. La sonda lo confirma al dar los mismos 32 nombres en orden inverso:
reorder:reversed_names -> Windows 11
Las dos rutas también fallan de forma distinta. Una consulta que lanza una excepción lleva todo el módulo a su rama de captura y devuelve una lista vacía, mientras que una carga fallida solo quita ese nombre de la lista cargada. Guardar las listas por separado es lo que permite distinguir un fallo del módulo entero de una familia que no cargó.
Dónde termina la inferencia
El orden de las dos búsquedas de plataforma también decide qué informa una máquina con fuentes de ambas tablas. getPlatformVersion prueba primero la búsqueda de Windows y solo recurre a macOS cuando Windows no devuelve nada, así que una Mac que por casualidad satisface todos los grupos de Windows se informa como Windows. La sonda lo hace concreto: un conjunto con los 14 nombres de Windows y los 18 de macOS devuelve Windows 11, no macOS Ventura.
Las tablas WindowsFonts y MacOSFonts son además una instantánea fija de las plataformas que al autor le importaban. Una versión nueva del sistema que traiga fuentes que la tabla no nombra es invisible para el módulo hasta que la tabla se actualice, así que la ausencia de coincidencia no es prueba de que la plataforma sea nueva.
La tabla de macOS es la única con etiquetas de versión de dos cifras, y las etiquetas son rangos y no valores exactos: 10.13-10.14 es un grupo y 10.15-11 es otro. Una coincidencia identifica por tanto un rango, no una versión puntual, y el informe debe conservar el rango en lugar de redondearlo a una sola versión.
La búsqueda se ejecuta después de construir la lista, así que un fallo en la detección produce una lista vacía y, con ella, ninguna respuesta de plataforma. Eso importa porque la respuesta ausente es un valor, no dos: una página que lee una versión vacía debería comprobar si la lista estaba vacía antes de concluir que la máquina no se reconoce, porque una lista vacía y una lista no vacía sin clave coincidente producen el mismo silencio.
El módulo publicado también expone unos pocos conjuntos de fuentes de aplicaciones, y cada uno se compara con la lista encontrada como grupo:
bucketsizes:DesktopAppFonts -> Microsoft Outlook=1 Adobe Acrobat=1 LibreOffice=4 OpenOffice=3
La tabla es pequeña, nueve nombres, y el conjunto de cada aplicación es más pequeño todavía. Tener las fuentes de una aplicación no es lo mismo que ejecutar la aplicación, porque las fuentes se pueden copiar o empaquetar por separado. El módulo trata un conjunto completo de fuentes de aplicación como evidencia de esa aplicación, y un informe debería presentarlo así en lugar de como prueba de que el programa está instalado.
Una página que llama al módulo dos veces en una misma sesión puede obtener dos listas, porque una fuente puede cargarse entre las llamadas. El módulo no guarda nada entre ejecuciones y reconstruye la lista cada vez, así que una segunda lectura es una observación nueva y no una vista refrescada de la primera.
La lista combinada es lo que la página comprueba de verdad, así que un nombre que aparece en la tabla de una plataforma se pregunta igual en todas. A una máquina Windows también se le pregunta si tiene los nombres de macOS, y la respuesta es casi siempre no. Así se mantienen independientes las tablas: la página pregunta por la unión y las búsquedas de plataforma leen la parte de la unión que les interesa.
La lista de fuentes también puede cambiar sin que el navegador se mueva: instalar una fuente para un usuario cambia lo que informa el navegador de ese usuario y deja intacta la lista de otro, y una actualización de paquetes puede añadir nombres que antes faltaban. La respuesta de plataforma sigue a la lista, así que se mueve con una instalación y no con la versión del sistema.
Las 14 devoluciones posibles también fijan el informe mínimo útil. Un solo campo de versión puede nombrar la respuesta pero no la razón, y quien quiera comprobar la inferencia necesita el conjunto de grupos que coincidió, no solo la versión que nombró. El informe debe guardar el digest de source y la lista de fuentes en un mismo registro, porque una lista sin la revisión no se puede comprobar y una revisión sin la lista no muestra qué encontró el navegador.
Las 14 devoluciones también fijan qué puede afirmar una comparación entre dos máquinas. Dos informes que dicen Windows 11 coinciden solo si coinciden los conjuntos de grupos guardados, porque la misma respuesta puede venir de una lista encontrada ligeramente distinta. Comparar las listas guardadas separa una diferencia real en las fuentes instaladas de las mismas fuentes leídas dos veces.
Registrar una lectura de fuentes
Una lectura de fuentes sostiene una afirmación estrecha: esta revisión de la fuente, este conjunto de tablas, produjo esta lista. Cuatro campos la mantienen comprobable:
- El digest de la fuente, porque un cambio en una tabla cambia los nombres que la página consulta y lo que significa una respuesta de versión.
- Los nombres que la página encontró, no solo la versión, porque la respuesta es una función de toda la lista encontrada y no se puede volver a deducir solo del nombre de la versión.
- Qué ruta de detección encontró cada nombre, porque las dos son observaciones separadas y la unión ordenada esconde la distinción.
- Los tamaños de los grupos, porque una respuesta de versión con un nombre y otra con cuatro no son la misma evidencia.
Fuentes públicas
- CreepJS
- Módulo de fuentes de CreepJS
- MDN: document.fonts
- MDN: local font load
- BotBrowser: referencia de coherencia de automatización
Conclusión práctica
Un registro que guarda el grupo Windows 11 que coincidió y las dos listas de rutas se puede repetir con un script corto; un registro que solo guarda la versión no se puede comprobar sin el navegador. Registra los nombres encontrados junto a la versión, porque la respuesta depende de un conjunto base completo y no se puede volver a deducir solo del nombre de la versión. Lee las dos rutas de detección como observaciones separadas, porque la unión ordenada esconde qué API encontró un nombre. Trata las 14 devoluciones posibles como el tamaño de la búsqueda y no como su precisión, porque un grupo de un solo nombre solo discrimina después de que todos los demás grupos del mapa hayan coincidido. Para cómo agrupa la biblioteca su señal de fuentes con los grupos de canvas y audio, lee los grupos de señales de CreepJS y cómo los combina; para el generador de canvas del mismo módulo, lee el ciclo de semilla de canvas de CreepJS.
Este artículo forma parte de la guía: ¿Qué es la huella digital del navegador?
Artículos Relacionados
Detección de Android WebView con CreepJS: límites
Huella digitalCómo leer las señales de huella del navegador
Huella digitalFingerprinting por medicion de texto: rastreo subpixel
Huella digital¿Qué es la huella de fuentes? Coherencia de texto entre plataformas
PlataformaPerfiles de navegador entre plataformas: una identidad en cada host
Huella digitalPredicción CreepJS: agregación y estados desconocidos
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.