Resumen
El fingerprinting de Canvas explota diferencias sutiles en cómo el hardware y el software renderizan gráficos. Estas diferencias surgen de:
- Modelo de GPU y versión del controlador
- Renderizado de fuentes del sistema operativo (anti-aliasing, hinting, renderizado subpíxel)
- Motor de renderizado del navegador
- Fuentes del sistema instaladas
Fingerprinting de Canvas
Mecanismo
- El sitio dibuja formas y texto en un elemento
<canvas>de HTML5 - Llama a
canvas.toDataURL()para extraer datos de píxeles como base64 - Hashea el resultado (MD5, SHA-1, o hash personalizado)
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
ctx.font = '14px Arial'
ctx.fillStyle = '#f60'
ctx.fillRect(125, 1, 62, 20)
ctx.fillStyle = '#069'
ctx.fillText('BrowserLeaks,com <canvas> 1.0', 2, 15)
const fingerprint = canvas.toDataURL()
const hash = md5(fingerprint) // único por dispositivo
Fuentes de Entropía
| Fuente | Variabilidad |
|---|---|
| Renderizado de fuentes (anti-aliasing, hinting) | Alta |
| Orden de renderizado subpíxel (RGB vs BGR) | Media |
| Rasterización GPU de formas | Media |
| Conversión de espacio de color | Baja |
Fingerprinting de WebGL
Mecanismo
WebGL expone la GPU directamente mediante:
const gl = canvas.getContext('webgl')
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info')
const renderer = debugInfo.UNMASKED_RENDERER_WEBGL
// ej., "NVIDIA GeForce RTX 3080/PCIe/SSE2"
const vendor = debugInfo.UNMASKED_VENDOR_WEBGL
// ej., "NVIDIA Corporation"
Además, el renderizado de escenas 3D produce salida de píxeles específica del hardware.
Señales
UNMASKED_RENDERER_WEBGL— modelo exacto de GPUUNMASKED_VENDOR_WEBGL— fabricante de GPU- Precisión de shader WebGL
MAX_TEXTURE_SIZE,MAX_VERTEX_ATTRIBS, etc.- Renderizado de escena 3D (hash de píxeles)
Fingerprinting de WebGPU
WebGPU (el sucesor de WebGL) expone información aún más granular:
const adapter = await navigator.gpu.requestAdapter()
const info = await adapter.requestAdapterInfo()
// { vendor: "nvidia", architecture: "ampere", device: "..." }
Señales
adapterInfo.vendoradapterInfo.architectureadapterInfo.device- Conjunto de características (qué funcionalidades GPU están soportadas)
Por Qué es Difícil la Falsificación a Nivel de Proxy
El renderizado de Canvas/WebGL/WebGPU ocurre en la GPU local. El proxy solo ve el tráfico de red. Cuando los datos llegan al proxy, el renderizado ya está completo.
Enfoques de mitigación posibles:
Bloquear la API: Hacer que WebGL devuelva null, que canvas lance un error. Problema: detectable como señal ("WebGL bloqueado" es en sí mismo un rasgo de fingerprint).
-
Inyectar ruido JS: CanvasBlocker (Firefox) aleatoriza la salida del canvas inyectando un píxel aleatorio antes de
toDataURL(). Problema: si el sitio lee el canvas 3 veces y obtiene 3 hashes diferentes, detecta la manipulación. También requiere ruido consistente en frecuencia. -
Anulación de datos de imagen mediante Proxy: Anular
CanvasRenderingContext2D.prototype.getImageDatamediante JS inyectado para devolver datos genéricos. Problema: solo funciona engetImageData, no entoDataURL. Múltiples rutas para extraer píxeles.
Renderizado GPU por software (SwiftShader/Lavapipe): Reemplazar el renderizado GPU con un renderizador determinista basado en CPU. Problema: costo masivo de rendimiento, sin aceleración 3D.
Fórmulas
Entropía del Fingerprint de Canvas
Donde es la probabilidad de un hash dado en diferentes dispositivos.
Entropía estimada: ~5-7 bits (puede distinguir ~32-128 dispositivos).
Probabilidad de Unicidad
Donde es el tamaño de la población y es el número de salidas de canvas distintas en la muestra.
Conclusiones
- El fingerprinting de Canvas/WebGL no puede falsificarse completamente a nivel de proxy
- La inyección de ruido mediante JS es una mitigación parcial pero detectable
- Bloquear estas APIs es detectable y se convierte en una señal en sí misma
- La única solución casi completa es el enfoque de Tor Browser (normalizar a todos los usuarios a una salida idéntica)
- Para Obscura: el mejor enfoque es JS inyectado que añade ruido consistente + bloquea
WEBGL_debug_renderer_info