AntiDetect
Funciones Tecnología Precios Para quién
Docs Blog FAQ
Idioma
EnglishItalianoDeutschEspañolРусский
Descargar

El ruido en el canvas es una huella digital en sí mismo

Aleatorizar el hash del canvas parece la defensa obvia. También es la forma más fiable de anunciar que estás usando un navegador antidetect — y el motivo es aritmética, no criptografía.

La defensa que se vuelve en tu contra

El fingerprinting del canvas funciona porque la rasterización por GPU no está estandarizada a nivel de píxel. Pide a dos máquinas que dibujen el mismo texto con la misma fuente y el mismo tamaño y el suavizado diferirá por un pelo. Haz el hash de los píxeles y obtienes un valor estable en un dispositivo y distinto en el siguiente.

La contramedida intuitiva es añadir ruido: alterar unos cuantos píxeles antes de calcular el hash, para que el valor cambie y no sirva para rastrearte. Casi todos los navegadores antidetect lo ofrecen, normalmente con la etiqueta «canvas: noise».

El problema es que el hash del canvas de un navegador real no cambia. Misma máquina, mismo navegador, misma página, mismo resultado — hoy, mañana y tras un reinicio. Esa estabilidad es justo la razón por la que merece la pena recoger la señal.

Así que un valor que cambia en cada lectura no se ha vuelto anónimo. Se ha vuelto anómalo. Es el mismo tipo de error que poner navigator.webdriver = false: la respuesta honesta te identifica, y la deshonesta te identifica como alguien que miente.

Cómo funciona de verdad la detección

Detectar la aleatorización no requiere nada ingenioso. Cuatro métodos, más o menos ordenados por lo a menudo que aparecen en la práctica:

Dibujar lo mismo dos veces

Renderizar un canvas idéntico dos veces en la misma carga de página y hashear ambos. Navegador real: idénticos. Ruido por llamada: distintos. Son dos líneas de código y están en todo script de detección serio.

Promediar el ruido hasta que desaparece

Si el ruido es aleatorio y de media cero, renderizar n veces y promediar empuja el resultado hacia el valor verdadero a un ritmo de aproximadamente √n. Veinte renderizados suelen bastar para recuperar el hash subyacente. La aleatorización le ha costado al rastreador unos milisegundos y a ti tu anonimato igualmente — encima marcándote como alguien que aleatoriza.

Comprobar si ese ruido encaja

Un canvas aleatorizado no es sospechoso por sí mismo. Brave lo trae de serie — su «farbling» deriva el ruido de una semilla por sesión y por origen. Tor Browser pregunta en su lugar. Lo que importa es si el ruido encaja con el navegador que dices ser. Chrome no aleatoriza el canvas. Un cliente cuyo user agent dice Chrome 147 y cuyo canvas cambia en cada llamada se ha contradicho, y la contradicción es el hallazgo, no el ruido.

Variar la ruta de renderizado

Las implementaciones ingenuas enganchan toDataURL() y getImageData(). Hay otras salidas: toBlob(), createImageBitmap(), el readPixels() de WebGL, OffscreenCanvas dentro de un worker. Si una ruta lleva ruido y otra no, las dos no cuadran — y ese desacuerdo es una prueba más fuerte que cualquiera de los dos valores por separado.

Gráfico de dispersión de la desviación del hash del canvas convergiendo al valor verdadero a medida que se promedian renderizados
El ruido de media cero se cancela solo. El error cae como 1/√n, así que veinte renderizados suelen bastar para recuperar el valor que la aleatorización escondía.

El camino de los workers en particular

Este último merece nota aparte, porque es donde se rompen casi todas las implementaciones.

OffscreenCanvas puede transferirse a un Web Worker y renderizarse allí. El worker tiene su propio ámbito global y su propia copia de las APIs de canvas, y una inyección de script a nivel de página no lo alcanza. Si tu ruido lo aplica JavaScript inyectado, el canvas del worker sale limpio.

const off = new OffscreenCanvas(280, 60);
const w = new Worker(url);
w.postMessage({ canvas: off }, [off]);
// the worker draws and hashes — untouched by any page-level hook

Ahora la página tiene dos hashes de canvas del mismo dispositivo que no coinciden. No existe configuración legítima en la que eso ocurra. Es el mismo problema estructural descrito en el artículo sobre CDP: lo que se aplica desde dentro de la página no puede alcanzar un contexto que la página no posee.

Cómo es una implementación correcta

El objetivo no es «un valor distinto cada vez». Es un valor distinto al de los demás, y el mismo valor siempre para mí. Dicho así, los requisitos salen solos:

PropiedadRequisito
Dentro de una misma carga de páginaIdéntico en cada renderizado
Entre sesionesIdéntico para el mismo perfil, siempre
Entre perfilesDistinto, y no por un desplazamiento constante
Entre rutas de renderizado2D, WebGL, OffscreenCanvas y worker coinciden
VerosimilitudCoherente con la GPU que el perfil declara

El mecanismo que satisface los cinco es una semilla. Fija una semilla aleatoria al crear el perfil, guárdala y deriva de ella la alteración de los píxeles de forma determinista. Cada renderizado de ese perfil produce la misma salida porque la semilla es la misma. Dos perfiles difieren porque difieren sus semillas. Y como la derivación ocurre donde se producen los píxeles — en el rasterizador, no en un método JavaScript enganchado —, todas las rutas reciben el mismo trato, workers incluidos.

Ese último punto es la razón de que esto sea difícil de hacer bien fuera de un motor modificado. No hay manera soportada de llegar al rasterizador de un worker desde un script de contenido.

El argumento para no tocarlo

Existe un argumento real, que merece exponerse con justicia, para no tocar el canvas en absoluto.

Si usas un perfil por máquina física, tu hash de canvas auténtico es un valor perfectamente corriente de un ordenador perfectamente corriente. Es estable, coherente en todas las rutas y encaja con tu GPU porque es tu GPU. Nada en él invita a mirar dos veces.

Eso deja de funcionar en el momento en que arrancas un segundo perfil en la misma máquina. Ahora los dos perfiles producen el mismo hash, y cualquier plataforma que haga fingerprinting del canvas puede relacionarlos — justo el resultado que querías evitar.

Así que la decisión no es «ruido o no ruido». Es:

Pruébalo tú mismo

No hace falta un proveedor de detección para comprobarlo. Abre la consola de tu navegador en cualquier página y ejecuta:

function h() {
  const c = document.createElement('canvas');
  const x = c.getContext('2d');
  x.textBaseline = 'top';
  x.font = '14px Arial';
  x.fillText('P8 canvas probe \u{1F511}', 2, 2);
  return c.toDataURL().slice(-32);
}
console.log(h(), h(), h());

Tres cadenas idénticas es lo que da un navegador real. Tres cadenas distintas significan ruido por llamada, y significan que cualquier web que ejecute esas mismas tres líneas lo sabe.

Luego ejecútalo otra vez tras reiniciar el perfil. El valor debería ser el mismo de antes si quieres que el perfil parezca un dispositivo y no un blanco móvil — y distinto del que producen tus otros perfiles. Contrasta el resultado con el resto del fingerprint: un canvas estable que implica una GPU que tus cadenas de WebGL no declaran solo ha movido la contradicción a otro sitio.

Estable por perfil, distinto entre perfiles

P8 deriva el canvas de cada perfil de una semilla fijada al crearlo. El mismo perfil siempre renderiza igual; dos perfiles nunca.

Empieza gratis
Todos los artículos Siguiente: fingerprinting de TLS y HTTP/2