Fingerprinting de rendimiento: senales de hardware
Cómo la precisión de Performance.now(), la concurrencia de hardware y la memoria del dispositivo se convierten en señales de rastreo, y cómo controlarlas a nivel del motor.
Prefieres la documentación del producto mantenida?
Este artículo tiene una página equivalente en el centro de documentación. Usa los docs para el flujo canónico, las flags actuales y la referencia duradera.
Introducción
La API Performance es una de las interfaces más fundamentales del navegador, diseñada originalmente para ayudar a los desarrolladores a medir tiempos de carga de páginas, diagnosticar cuellos de botella y optimizar la experiencia del usuario. Funciones como performance.now(), performance.mark() y performance.measure() proporcionan marcas de tiempo de alta resolución para medir la ejecución de código. Junto con estas funciones de temporización, propiedades como navigator.hardwareConcurrency y navigator.deviceMemory reportan el conteo de núcleos de CPU del dispositivo y la RAM disponible.
Si bien estas API sirven propósitos legítimos, también exponen características específicas del hardware que varían entre dispositivos. Un portátil con 8 núcleos de CPU y 16 GB de RAM produce resultados de temporización y reportes de hardware mediblemente diferentes a un escritorio con 16 núcleos y 32 GB. Estas diferencias permanecen estables entre sesiones del navegador, sobreviven al borrado de cookies y persisten a través del modo incógnito, convirtiéndolas en señales confiables para rastrear dispositivos individuales.
Impacto en la privacidad
Las señales de temporización de rendimiento representan una forma particularmente insidiosa de fingerprinting porque están profundamente ligadas a las características físicas del hardware que los usuarios no pueden cambiar fácilmente. A diferencia de las cookies o el almacenamiento local, que los usuarios pueden borrar, las señales de temporización emergen de la velocidad fundamental a la que un dispositivo ejecuta JavaScript, renderiza gráficos y procesa datos.
Investigaciones de instituciones incluyendo Princeton y KU Leuven han demostrado que el fingerprinting basado en temporización puede distinguir dispositivos con alta precisión. Un estudio de 2019 mostró que combinar mediciones de performance.now() con valores de concurrencia de hardware podía identificar correctamente a visitantes recurrentes más del 80% de las veces, incluso cuando otros identificadores se habían borrado.
El problema se extiende más allá de señales individuales. Cuando un rastreador recopila navigator.hardwareConcurrency (núcleos de CPU), navigator.deviceMemory (RAM) y tiempos de ejecución de microbenchmarks juntos, la huella combinada se vuelve altamente distintiva. Un dispositivo con 6 núcleos, 8 GB de memoria y un perfil específico de velocidad de ejecución de JavaScript reduce significativamente la ventana de identificación. Estas señales no requieren permisos de usuario, no generan avisos y son invisibles para la persona que está siendo rastreada.
Contexto técnico
Para entender por qué la temporización de rendimiento varía entre dispositivos, es útil observar qué influye en la velocidad de ejecución de JavaScript y las API que exponen información del hardware.
Performance.now() y tiempo de alta resolución
performance.now() devuelve una marca de tiempo de alta resolución medida en milisegundos con precisión de microsegundos. La resolución real depende de las mitigaciones de seguridad del navegador (muchos navegadores reducen la precisión para prevenir ataques tipo Spectre), pero incluso la precisión reducida revela características de temporización ligadas a la velocidad del reloj de la CPU, jerarquía de caché y pipeline de instrucciones.
Cuando un sitio web ejecuta un microbenchmark, como iterar un bucle 100,000 veces o calcular hashes SHA-256, el tiempo de ejecución está directamente influenciado por el hardware del dispositivo. Una CPU de escritorio rápida completa el mismo trabajo más rápido que un procesador móvil, y esta diferencia es medible y repetible.
Concurrencia de hardware
navigator.hardwareConcurrency devuelve el número de núcleos de procesador lógicos disponibles para el navegador. Este valor refleja la configuración física de la CPU y no cambia entre sesiones. Los valores comunes van desde 2 (móvil de gama baja) hasta 32 o más (estaciones de trabajo de alto rendimiento). La distribución no es uniforme: ciertos conteos de núcleos (4, 8, 12, 16) son mucho más comunes que otros, lo que significa que los valores inusuales se convierten en identificadores fuertes.
Memoria del dispositivo
navigator.deviceMemory devuelve una cantidad aproximada de RAM del dispositivo en gigabytes, redondeada a la potencia de dos más cercana (0.25, 0.5, 1, 2, 4, 8). Aunque el redondeo reduce la precisión, este valor sigue contribuyendo a la huella digital general. Un dispositivo que reporta 8 GB de memoria combinado con 8 núcleos es una huella diferente a uno que reporta 4 GB con 4 núcleos.
Por qué estos valores importan juntos
Individualmente, cada señal tiene entropía limitada. Pero combinadas, forman un perfil de hardware: una velocidad de ejecución específica, un conteo de núcleos específico y una clase de memoria específica. Cuando se superponen con otras señales de huella digital como Canvas, WebGL y fuentes, las características de temporización contribuyen significativamente a la identificación del dispositivo.
Enfoques de protección comunes y sus limitaciones
VPN y servidores proxy
Las VPN cambian tu dirección IP pero tienen cero efecto en la temporización de rendimiento. Todas las mediciones de temporización y consultas de propiedades de hardware ocurren localmente dentro del navegador. Dos dispositivos detrás de la misma VPN siguen reportando perfiles de hardware y velocidades de ejecución completamente diferentes.
Incógnito y navegación privada
Los modos de navegación privada borran cookies e historial pero no modifican navigator.hardwareConcurrency, navigator.deviceMemory ni la velocidad de ejecución del hardware subyacente. Tu huella de rendimiento en incógnito es idéntica a tu huella en una ventana normal.
Extensiones de navegador
Las extensiones que intentan modificar propiedades de hardware enfrentan limitaciones fundamentales. Pueden anular valores de propiedades JavaScript inyectando scripts, pero esto crea inconsistencias que son directas de detectar:
- Si
navigator.hardwareConcurrencyreporta 4 núcleos pero un microbenchmark de Web Worker muestra 8 hilos paralelos ejecutándose simultáneamente, los valores entran en conflicto. - Si
navigator.deviceMemoryreporta 2 GB pero la temporización deperformance.now()para operaciones intensivas en memoria es consistente con 16 GB (sin pausas de recolección de basura), la discrepancia es aparente. - Las anulaciones inyectadas por extensiones pueden detectarse verificando descriptores de propiedades, cadenas de prototipos o ejecutando código en un contexto limpio antes de que las extensiones se ejecuten.
Aleatorización
Algunas herramientas de privacidad aleatorizan valores de temporización añadiendo ruido a performance.now(). Si bien esto previene la identificación estable, desplazar aleatoriamente las marcas de tiempo rompe el monitoreo legítimo de rendimiento y crea su propio patrón detectable. Una secuencia de marcas de tiempo con jitter artificial se ve diferente de la variación natural del hardware.
Enfoque a nivel del motor de BotBrowser
BotBrowser toma un enfoque fundamentalmente diferente para la protección de temporización de rendimiento. En lugar de interceptar llamadas a la API o añadir ruido después del hecho, BotBrowser controla las señales de temporización a nivel del motor del navegador para producir resultados internamente consistentes que coinciden con un perfil completo del dispositivo.
Identidad de hardware basada en perfiles
Cuando cargas un perfil de huella digital, BotBrowser configura todos los valores relacionados con hardware para coincidir con el dispositivo objetivo de ese perfil:
chrome --bot-profile="/path/to/profile.enc" \
--user-data-dir="$(mktemp -d)"
Esto establece navigator.hardwareConcurrency y navigator.deviceMemory a los valores configurados del perfil. Estas no son anulaciones de JavaScript; se aplican a nivel del motor antes de que cualquier código de página se ejecute. Las verificaciones de descriptores de propiedades, inspección de prototipos y evaluación en contexto limpio devuelven los mismos valores porque el motor mismo los reporta.
Escala de temporización de rendimiento
BotBrowser proporciona el flag --bot-time-scale para controlar la velocidad de ejecución aparente de las operaciones JavaScript:
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=0.92
El flag acepta un valor de punto flotante inferior a 1.0 (rango tipico: 0.80-0.99). Un valor de escala de 0.92 comprime los intervalos de performance.now() en un 8%, reduciendo las senales de sesgo temporal que podrian identificar el dispositivo. La escala se aplica uniformemente a todas las fuentes de temporizacion, por lo que los microbenchmarks, performance.mark() y performance.measure() producen resultados internamente consistentes que coinciden con la misma clase de hardware.
Semillas de temporización de rendimiento
Introducido en marzo de 2026, --bot-time-seed (ENT Tier2) proporciona una protección de temporización de rendimiento mas granular. Mientras que --bot-time-scale comprime los intervalos de temporizacion para reducir senales de sesgo, --bot-time-seed controla la distribucion de temporizacion entre las operaciones individuales del navegador.
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=1.0 \
--bot-time-seed=12345
--bot-time-seed=<integer> acepta una semilla entera desde 1 hasta UINT32_MAX (0 desactiva la funcionalidad). Cada semilla produce un perfil de rendimiento unico y estable que diversifica la temporizacion en multiples operaciones del navegador, asegurando que cada instancia tenga una firma de temporizacion distinta y consistente.
La semilla también cubre la redistribución por sesión de los valores de performance.getEntries(), performance.getEntriesByType("navigation") y performance.timing, haciendo que los patrones de temporización de navegación sean únicos para cada semilla.
Esto es completamente determinístico: la misma semilla siempre produce la misma huella de temporización. Combinado con --bot-time-scale, obtienes control de velocidad a nivel macro y diversidad de temporización a nivel micro.
Ejemplo con Playwright:
const { chromium } = require('playwright-core');
(async () => {
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-time-scale=1.0',
'--bot-time-seed=12345',
],
headless: true,
});
const context = await browser.newContext({ viewport: null });
const page = await context.newPage();
await page.goto('https://example.com');
// La distribución de temporización es única para la semilla 12345
await browser.close();
})();
Ejemplo con Puppeteer:
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-time-scale=1.0',
'--bot-time-seed=12345',
],
headless: true,
defaultViewport: null,
});
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
})();
Consistencia entre señales
La ventaja clave del control a nivel del motor es la consistencia. Cuando BotBrowser reporta 4 núcleos de CPU y 4 GB de memoria a través de un perfil, las características de temporización de la ejecución de JavaScript también se alinean con esa clase de hardware. Los Web Workers se ejecutan con paralelismo consistente con el conteo de núcleos reportado. Los patrones de asignación de memoria coinciden con la clase de memoria reportada. No hay contradicciones entre lo que el navegador reporta y cómo realmente funciona.
Modo determinístico
Para pruebas, investigación y pipelines CI/CD, BotBrowser soporta temporización completamente determinística a través del flag de noise seed:
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=1.0 \
--bot-noise-seed=42 \
--user-data-dir="$(mktemp -d)"
El mismo perfil, escala de tiempo y noise seed producen huellas de temporización idénticas entre sesiones, máquinas anfitrionas y sistemas operativos.
Configuración y uso
Uso básico de CLI
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=1.0 \
--user-data-dir="$(mktemp -d)"
Integración con Playwright
const { chromium } = require('playwright-core');
(async () => {
const browser = await chromium.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-time-scale=1.0',
],
headless: true,
});
const context = await browser.newContext({ viewport: null });
const page = await context.newPage();
await page.goto('https://example.com');
// Los valores de hardware y señales de temporización coinciden con el perfil cargado
await browser.close();
})();
Integración con Puppeteer
const puppeteer = require('puppeteer-core');
(async () => {
const browser = await puppeteer.launch({
executablePath: '/path/to/botbrowser/chrome',
args: [
'--bot-profile=/path/to/profile.enc',
'--bot-time-scale=0.92',
'--bot-noise-seed=42',
],
headless: true,
defaultViewport: null,
});
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
})();
Configuración combinada
Para protección integral, combina el control de temporización con otras configuraciones del perfil:
chrome --bot-profile="/path/to/profile.enc" \
--bot-time-scale=1.0 \
--bot-noise-seed=42 \
--proxy-server="socks5://user:pass@proxy:1080" \
--bot-config-timezone="America/New_York" \
--bot-config-locale="en-US"
Verificación
Después de lanzar BotBrowser con un perfil, verifica que los valores de hardware y la temporización estén controlados:
// Verificar propiedades de hardware
console.log('CPU Cores:', navigator.hardwareConcurrency);
console.log('Device Memory:', navigator.deviceMemory, 'GB');
// Medir consistencia de temporización
async function measureTiming() {
const results = [];
for (let i = 0; i < 5; i++) {
const start = performance.now();
for (let j = 0; j < 100000; j++) Math.sqrt(j);
results.push(performance.now() - start);
}
return results;
}
const timings = await measureTiming();
console.log('Timing samples:', timings);
console.log('Variance:', Math.max(...timings) - Math.min(...timings));
Qué verificar:
navigator.hardwareConcurrencycoincide con el conteo de núcleos configurado del perfilnavigator.deviceMemorycoincide con la memoria configurada del perfil- Las muestras de temporización muestran comportamiento consistente entre ejecuciones con el mismo perfil y semilla
- Las herramientas públicas de prueba de huella digital (CreepJS, BrowserLeaks) no muestran anomalías
Mejores prácticas
-
Siempre usa un perfil completo. Los valores de hardware, temporización y otras señales deben estar todas alineadas. Un perfil asegura consistencia en cada superficie de huella digital.
-
Usa
--bot-time-scaleintencionalmente. Establece la escala para reducir el sesgo temporal de la clase de hardware objetivo del perfil. Los valores tipicos van de 0.80 a 0.99. Los valores mas bajos comprimen los intervalos de temporizacion de forma mas agresiva. -
Usa el modo determinístico para CI/CD. Combina
--bot-noise-seedcon--bot-time-scalepara resultados reproducibles en pipelines de pruebas automatizadas. -
Haz coincidir la geografía del proxy con la identidad del perfil. Un perfil configurado para un dispositivo en EE.UU. debería usar un proxy de EE.UU. La consistencia de temporización combinada con geolocalización no coincidente debilita la coherencia general de la huella digital.
-
Evita mezclar extensiones de navegador que modifiquen la temporización. BotBrowser maneja toda la protección de temporización nativamente a nivel del motor. Las extensiones de terceros que modifican
performance.now()o propiedades de hardware entrarán en conflicto.
Preguntas frecuentes
¿La precisión de performance.now() difiere entre navegadores?
Sí. Diferentes navegadores implementan diferentes niveles de precisión de marca de tiempo, en parte como mitigación contra ataques de canal lateral tipo Spectre. Chrome, Firefox y Safari cada uno reduce la precisión de forma diferente. El sistema de perfiles de BotBrowser tiene en cuenta las características de precisión esperadas del navegador objetivo.
¿Los sitios web pueden detectar que los valores de temporización están siendo controlados?
Si el control de temporización se aplica de forma inconsistente (por ejemplo, modificando performance.now() pero no Date.now() o performance.mark()), los sitios web pueden detectar la discrepancia. BotBrowser aplica la escala de temporización uniformemente a todas las fuentes de temporización a nivel del motor, previniendo inconsistencias entre API.
¿--bot-time-scale afecta el rendimiento real de las páginas?
No. El flag controla cómo se reportan los valores de temporización, no cuán rápido se ejecuta realmente JavaScript. Las páginas cargan y se ejecutan a velocidad completa; solo las mediciones reportadas a JavaScript reflejan la temporización escalada.
¿Cómo interactúa la concurrencia de hardware con los Web Workers?
BotBrowser asegura que el valor reportado de navigator.hardwareConcurrency sea consistente con el número de Web Workers que pueden ejecutarse en paralelo. El perfil gestiona ambos valores juntos.
¿Puedo usar diferentes escalas de tiempo para diferentes perfiles?
Sí. Cada instancia del navegador usa su propio valor de --bot-time-scale. Puedes ejecutar múltiples instancias simultáneamente con diferentes perfiles y diferentes características de temporización.
¿Qué pasa con la temporización de SharedArrayBuffer?
SharedArrayBuffer combinado con Atomics puede usarse como temporizador de alta resolución. El control de temporización a nivel del motor de BotBrowser se aplica también a los canales de temporización de memoria compartida, manteniendo consistencia a través de todos los métodos de medición de temporización.
¿Las huellas de temporización sobreviven a las actualizaciones del navegador?
En navegadores sin protección, sí. La huella de temporización está ligada al hardware, no a la versión del navegador. Los perfiles de BotBrowser están versionados, por lo que puedes mantener características de temporización consistentes a través de actualizaciones de perfil.
¿Cuál es la diferencia entre --bot-time-seed y --bot-time-scale?
Sirven propositos complementarios. --bot-time-scale comprime los intervalos de performance.now() (acepta un valor de punto flotante inferior a 1.0, rango tipico 0.80-0.99) para reducir las senales de sesgo temporal. --bot-time-seed controla la distribucion de temporizacion en las operaciones individuales del navegador, produciendo un perfil de temporizacion por operacion unico para cada valor de semilla. Puedes usar ambos juntos: --bot-time-scale establece la compresion de temporizacion a nivel macro, mientras que --bot-time-seed agrega diversidad de temporizacion a nivel micro dentro de ese perfil.
Resumen
Las API de temporización de rendimiento exponen características de hardware que sirven como señales de rastreo estables. performance.now(), navigator.hardwareConcurrency y navigator.deviceMemory juntos forman una identidad de hardware que persiste entre sesiones y sobrevive a las medidas de privacidad comunes. BotBrowser controla todas estas senales a nivel del motor del navegador a traves de su sistema de perfiles, --bot-time-scale para la compresion de intervalos de temporizacion, y --bot-time-seed para la diversidad de temporizacion por operacion. Juntos, estos flags aseguran resultados internamente consistentes que coinciden con la identidad del dispositivo objetivo. Para protección relacionada, consulta protección de huella Canvas, control de huella WebGL y control de tasa de cuadros.
Artículos Relacionados
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.