Huella digital

Fingerprinting de tasa de cuadros: frecuencia

Cómo el timing de requestAnimationFrame y las tasas de actualización de pantalla crean señales de huella, y técnicas para controlar la tasa de cuadros a nivel del motor.

Documentación

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 requestAnimationFrame fue diseñada para ayudar a los desarrolladores a crear animaciones fluidas sincronizando las operaciones de dibujo con el ciclo de actualización de la pantalla. En lugar de usar temporizadores arbitrarios como setTimeout, requestAnimationFrame llama a una función antes del siguiente repintado de pantalla, típicamente a la tasa de actualización nativa del monitor. Esto resulta en animaciones más suaves, mejor eficiencia de batería y una experiencia de usuario más responsiva.

Sin embargo, la frecuencia de callback de requestAnimationFrame revela directamente la tasa de actualización de la pantalla. Un monitor estándar de 60 Hz dispara callbacks aproximadamente cada 16.67 milisegundos, mientras que un monitor gaming de 144 Hz los dispara cada 6.94 milisegundos. Una pantalla de 120 Hz, una de 240 Hz y una pantalla con tasa de actualización variable (VRR) producen intervalos de callback mensurablemente diferentes. Esta información es accesible para cualquier sitio web sin permisos, convirtiéndola en una señal de fingerprinting fiable y persistente.

Impacto en la privacidad

La tasa de actualización de pantalla es una característica de hardware que los usuarios raramente consideran en el contexto de la privacidad. A diferencia de las cookies o direcciones IP, la tasa de actualización está ligada al hardware físico del monitor y no cambia entre sesiones del navegador, modos de navegación privada o después de borrar datos del sitio.

El riesgo de fingerprinting ha aumentado significativamente a medida que la tecnología de pantallas se ha diversificado. Hace una década, casi todos los monitores de consumo funcionaban a 60 Hz, proporcionando poca información distinguidora. Hoy, el mercado incluye paneles de 60, 75, 90, 120, 144, 165, 240 y 360 Hz. Las configuraciones multi-monitor pueden reportar diferentes tasas dependiendo de qué pantalla ocupa la ventana del navegador. Los portátiles con funciones de tasa de actualización dinámica (como ProMotion de Apple a 120 Hz) añaden más variación.

Esta diversidad significa que la información de tasa de cuadros ahora lleva entropía significativa. Un visitante con un monitor de 165 Hz es significativamente más raro que uno con una pantalla de 60 Hz, haciendo la tasa de actualización una señal valiosa para reducir la identidad. Cuando se combina con resolución de pantalla, profundidad de color, información de GPU y otras señales relacionadas con la pantalla, la tasa de cuadros contribuye a un perfil de hardware detallado.

El problema se agrava porque múltiples APIs filtran esta información. Además del timing de requestAnimationFrame, las animaciones CSS, la API de Screen e incluso el timing de callbacks de setTimeout (que el navegador alinea al ciclo de la pantalla) pueden revelar la tasa de actualización.

Trasfondo técnico

Cómo requestAnimationFrame expone la tasa de actualización

Cuando un sitio web llama a requestAnimationFrame(callback), el navegador invoca la función callback antes de cada repintado de pantalla. El callback recibe un parámetro DOMHighResTimeStamp indicando cuándo comenzó el frame. Midiendo el intervalo entre timestamps consecutivos, un sitio web puede determinar la tasa de actualización de la pantalla con alta precisión.

La medición es directa: recopilar entre 30 y 60 timestamps de frames, calcular el intervalo promedio y derivar la frecuencia. Con tan solo 10 frames, la tasa de actualización puede determinarse de forma fiable. La medición toma menos de 200 milisegundos en una pantalla de 60 Hz y es invisible para el usuario.

Animaciones y transiciones CSS

Las animaciones CSS se ejecutan a la tasa de actualización de la pantalla por defecto. Una animación @keyframes que dura exactamente un segundo producirá 60 frames intermedios en una pantalla de 60 Hz y 144 frames en una de 144 Hz. Aunque la especificación CSS no garantiza precisión frame a frame, el timing de eventos de animación (animationstart, animationend, timing de transiciones) aún filtra información de tasa de actualización.

Complejidad de tasa de actualización variable

Las pantallas modernas con tecnología de tasa de actualización variable (G-Sync, FreeSync, ProMotion) pueden cambiar su tasa de actualización dinámicamente según el contenido. Esto crea una dimensión adicional de fingerprinting: no solo la tasa de actualización base sino el patrón de comportamiento adaptativo se convierte en una señal.

Configuraciones multi-monitor

Cuando una ventana del navegador abarca múltiples monitores o se mueve entre pantallas con diferentes tasas de actualización, el timing de requestAnimationFrame cambia correspondientemente. Este patrón de transición puede revelar configuraciones multi-monitor, que son en sí una señal de fingerprinting.

Enfoques comunes de protección y sus limitaciones

VPNs y servidores proxy

Las VPN no tienen efecto en la tasa de actualización de la pantalla. La tasa de cuadros es una propiedad de hardware local medida enteramente dentro de la tubería de renderizado del navegador. Las herramientas de privacidad a nivel de red no pueden modificarla.

Modo incógnito y navegación privada

Los modos de navegación privada no alteran la tasa de actualización de la pantalla ni el comportamiento de requestAnimationFrame. La huella de tasa de cuadros en modo incógnito es idéntica a la huella en una ventana normal.

Extensiones del navegador

Las extensiones que intentan modificar el comportamiento de requestAnimationFrame enfrentan desafíos significativos:

  • Envolver el callback: Una extensión puede interceptar requestAnimationFrame y limitar callbacks a una tasa objetivo (ej. 60 FPS). Sin embargo, esto crea tartamudeo visible en animaciones legítimas y es detectable comparando el timing de requestAnimationFrame con el timing de animaciones CSS, que la extensión puede no controlar.
  • Modificar timestamps: Alterar el DOMHighResTimeStamp pasado a callbacks puede simular una tasa de actualización diferente, pero las inconsistencias con performance.now(), Date.now() y el timing de transiciones CSS revelan la modificación.
  • Bloquear la API: Deshabilitar requestAnimationFrame completamente rompe la mayoría de aplicaciones web modernas que dependen de ella para renderizado.

El problema fundamental es que las extensiones operan en la capa de API JavaScript y no pueden controlar la tubería de renderizado real. La tasa de actualización real de la pantalla influye en múltiples subsistemas del navegador simultáneamente, y modificar una API sin controlar todas las demás crea inconsistencias detectables.

Limitación a nivel de navegador

Algunos navegadores ofrecen limitación de tasa de cuadros en configuraciones de desarrollador. Este enfoque limita la tasa de renderizado pero no proporciona control detallado, no puede establecerse por sesión y frecuentemente introduce artefactos visuales que indican limitación.

Enfoque de BotBrowser a nivel de motor

BotBrowser controla la tasa de cuadros a nivel de la tubería de renderizado del motor del navegador a través del flag --bot-fps. Esto no es un wrapper JavaScript o interceptor de API. El bucle de renderizado opera a la tasa especificada, asegurando que todas las señales dependientes de la tasa de cuadros sean internamente consistentes.

Tasa de cuadros basada en perfil

Cuando se usa el modo profile, BotBrowser lee la tasa de cuadros objetivo del perfil de huella digital cargado:

chrome --bot-profile="/path/to/profile.enc" \
       --bot-fps=profile \
       --user-data-dir="$(mktemp -d)"

El perfil define la tasa de actualización esperada de la pantalla según el dispositivo objetivo. Un perfil que representa un portátil de oficina estándar reporta 60 FPS. Un perfil de escritorio gaming podría reportar 144 FPS. La tubería de renderizado funciona a esta tasa, por lo que los callbacks de requestAnimationFrame, animaciones CSS y todo el timing dependiente de frames se alinean con las características de pantalla del perfil.

Tasa de cuadros personalizada

Para casos de uso específicos, puedes establecer una tasa de cuadros exacta:

chrome --bot-profile="/path/to/profile.enc" \
       --bot-fps=60

Esto fuerza la tubería de renderizado a 60 FPS independientemente de la tasa de actualización real de la pantalla del host. Todas las mediciones dependientes de timing reportarán aproximadamente 16.67 ms por frame.

Tasa real de pantalla

Cuando quieres que el navegador use la tasa de actualización real de la pantalla:

chrome --bot-profile="/path/to/profile.enc" \
       --bot-fps=real

Consistencia a nivel de motor

Porque el control se aplica a nivel de la tubería de renderizado, todas las señales dependientes de tasa de cuadros son consistentes:

  • Los callbacks de requestAnimationFrame se disparan a la tasa controlada
  • Los timestamps de frame coinciden con el intervalo esperado
  • Las animaciones CSS avanzan al ritmo correcto
  • El timing de performance.now() durante frames de animación se alinea con la tasa reportada
  • No hay discrepancias entre la tasa de cuadros medida por JavaScript y el comportamiento de renderizado real

Configuración y uso

Uso básico por CLI

# Use profile-defined frame rate
chrome --bot-profile="/path/to/profile.enc" \
       --bot-fps=profile \
       --user-data-dir="$(mktemp -d)"

# Fixed 60 FPS
chrome --bot-profile="/path/to/profile.enc" \
       --bot-fps=60

# Actual display rate
chrome --bot-profile="/path/to/profile.enc" \
       --bot-fps=real

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-fps=60',
    ],
    headless: true,
  });

  const context = await browser.newContext({ viewport: null });
  const page = await context.newPage();
  await page.goto('https://example.com');
  // requestAnimationFrame will fire at 60 FPS
  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-fps=profile',
    ],
    headless: true,
    defaultViewport: null,
  });

  const page = await browser.newPage();
  await page.goto('https://example.com');
  await browser.close();
})();

Combinado con protección de timing

Para control completo de señales de hardware, combina control de tasa de cuadros con escala de timing:

chrome --bot-profile="/path/to/profile.enc" \
       --bot-fps=60 \
       --bot-time-scale=1.0 \
       --bot-noise-seed=42 \
       --user-data-dir="$(mktemp -d)"

Verificación

Después de lanzar BotBrowser con una tasa de cuadros configurada, verifica el resultado:

const measuredFps = await page.evaluate(() => {
  return new Promise(resolve => {
    const timestamps = [];
    function frame(ts) {
      timestamps.push(ts);
      if (timestamps.length < 61) {
        requestAnimationFrame(frame);
      } else {
        const intervals = [];
        for (let i = 1; i < timestamps.length; i++) {
          intervals.push(timestamps[i] - timestamps[i - 1]);
        }
        const avgInterval = intervals.reduce((a, b) => a + b) / intervals.length;
        resolve({
          fps: Math.round(1000 / avgInterval),
          avgInterval: avgInterval.toFixed(2),
          minInterval: Math.min(...intervals).toFixed(2),
          maxInterval: Math.max(...intervals).toFixed(2),
        });
      }
    }
    requestAnimationFrame(frame);
  });
});

console.log('Measured FPS:', measuredFps.fps);
console.log('Average interval:', measuredFps.avgInterval, 'ms');

Qué verificar:

  1. Los FPS medidos coinciden con el valor configurado (ej. 60 para --bot-fps=60)
  2. Los intervalos entre frames son consistentes con la tasa objetivo
  3. Múltiples métodos de medición (requestAnimationFrame, timing de animaciones CSS) producen el mismo resultado
  4. Las herramientas de prueba de huellas digitales reportan la tasa de actualización esperada

Mejores prácticas

  1. Haz coincidir los FPS con el hardware del perfil. Un perfil que representa un portátil de oficina estándar debería usar 60 FPS. Un perfil de escritorio gaming puede usar 120 o 144. Valores no coincidentes debilitan la coherencia de la huella digital.

  2. Usa --bot-fps=profile para flujos automatizados. Esto selecciona automáticamente la tasa de cuadros correcta del perfil, reduciendo errores de configuración.

  3. Combina con --bot-time-scale. La tasa de cuadros y el timing de ejecución deberían alinearse. Un perfil de dispositivo lento con tasa de cuadros de 60 FPS debería tener características de timing que coincidan con hardware de gama baja.

  4. Evita valores de FPS arbitrarios. Elige valores que correspondan a hardware de pantalla real: 24, 30, 48, 60, 75, 90, 120, 144, 165, 240. Valores como 73 o 137 no corresponden a ningún monitor real y destacarían.

  5. Prueba en modo headless. Los navegadores headless no tienen pantalla física, por lo que la tasa de cuadros está completamente determinada por la configuración de BotBrowser. Verifica que los FPS configurados se reportan correctamente en modo headless.

Preguntas frecuentes

¿La huella de tasa de cuadros funciona en dispositivos móviles?

Sí. Los dispositivos móviles tienen tasas de actualización variables (60 Hz, 90 Hz, 120 Hz), y muchos teléfonos modernos soportan cambio dinámico de tasa de actualización. El timing de requestAnimationFrame revela la tasa de actualización actual en navegadores móviles igual que en escritorio.

¿Pueden los sitios web detectar que los FPS están siendo controlados?

Si el control se aplica a nivel de JavaScript (envolviendo requestAnimationFrame), sí, porque las animaciones CSS y otras fuentes de timing pueden reportar una tasa diferente. BotBrowser controla los FPS a nivel de la tubería de renderizado, por lo que todas las fuentes de timing son consistentes.

¿--bot-fps afecta la reproducción de video?

La reproducción de video usa una tubería de decodificación separada y no se ve afectada por el flag --bot-fps. Los videos se reproducen a su tasa de cuadros codificada. Solo el bucle de renderizado y el timing de animaciones se controlan.

¿Qué pasa con contenido de tasa de actualización variable?

BotBrowser aplica una tasa de cuadros fija basada en la configuración. Esto es consistente con cómo se comportan la mayoría de los navegadores reales cuando se usa un monitor de tasa fija, que sigue siendo la mayoría de las pantallas.

¿Puedo usar diferentes valores de FPS para diferentes pestañas?

El flag --bot-fps se aplica a toda la instancia del navegador. Para diferentes tasas de cuadros, lanza instancias de navegador separadas con diferentes configuraciones.

¿Cómo interactúa la tasa de cuadros con la API visibility?

Los navegadores típicamente limitan requestAnimationFrame para pestañas en segundo plano. BotBrowser respeta este comportamiento a menos que se use --bot-always-active, que mantiene todas las pestañas ejecutándose a la tasa de cuadros configurada independientemente del estado de visibilidad.

¿La propiedad Screen.refreshRate coincide?

En navegadores que exponen screen.refreshRate o propiedades similares, BotBrowser asegura que estos valores sean consistentes con la tasa de cuadros configurada por --bot-fps y el perfil cargado.

Resumen

La tasa de actualización de la pantalla, mensurable a través del timing de requestAnimationFrame, animaciones CSS y APIs relacionadas, es una señal de fingerprinting de hardware persistente que las herramientas de privacidad estándar no pueden abordar. BotBrowser controla la tasa de cuadros a nivel de la tubería de renderizado del motor del navegador a través del flag --bot-fps, asegurando que todas las señales dependientes de frames sean internamente consistentes. Combinado con protección de timing de rendimiento, control de Canvas y gestión integral de perfiles, BotBrowser proporciona protección completa de señales de hardware para flujos de trabajo centrados en la privacidad.

#Fps#Frame-Rate#requestAnimationFrame#fingerprinting#Privacy#Display

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.