4 puntos por GN⁺ 2023-11-03 | 1 comentarios | Compartir por WhatsApp
  • Bear Blog implementó su propio sistema de analítica que funciona sin JavaScript del lado del cliente, debido a restricciones de velocidad, eficiencia y estabilidad
  • Los scripts de analítica habituales pueden ser bloqueados por bloqueadores de anuncios, y con solo los logs del servidor pueden mezclarse rastreadores, scrapers e incluso parsers basados en GPT, lo que distorsiona las estadísticas de visitas
  • En el CSS de cada página, cuando ocurre body:hover, se llama al endpoint /hit/{{ post.id }}/ mediante border-image, usando el hover o el scroll en móvil como señal de lectura
  • El servidor usa el user-agent para verificar si es un bot, el navegador y la plataforma, y la dirección IP solo se usa para determinar el país; luego elimina lecturas duplicadas con un hash de IP+fecha
  • Si se lee desde varios dispositivos el mismo día con la misma IP, se cuenta como una sola vez, pero aun sin guardar información identificable se puede contabilizar de forma simple el número de lecturas únicas por página

Crear eventos de lectura con hover de CSS

  • El sistema de analítica de Bear Blog sigue la restricción de no usar JavaScript del lado del cliente
  • Las herramientas de analítica convencionales pueden juzgar parcialmente la autenticidad del tráfico o si se trata de bots con JavaScript del lado del cliente, pero muchos bloqueadores de anuncios bloquean no solo Google Analytics, sino también scripts de analítica como Fathom o Plausible
  • Parsear únicamente los logs del servidor puede mezclar rastreadores de motores de búsqueda, scrapers y parsers basados en GPT como si fueran tráfico normal, generando una visión distorsionada
  • Bear inserta el siguiente CSS en cada página para que, cuando el usuario pase el cursor sobre la página o haga scroll en móvil, se active body:hover
body:hover {
  border-image: url("/hit/{{ post.id }}/?ref={{ request.META.HTTP_REFERER }}");
}
  • Cuando se activa body:hover, se llama a la URL de hit de ese artículo
  • La información que se vuelve a agregar explícitamente en esta solicitud es el referrer, y la ortografía HTTP_REFERER es una forma con un error ortográfico que quedó como estándar
  • Aprovechando que los bots no hacen hover, Bear usa la llamada basada en body:hover como señal de lector humano
  • Después, en el servidor se comprueba si el user-agent corresponde a un bot y se extraen de la cadena del user-agent el navegador y la plataforma

Eliminar lecturas duplicadas sin información identificable

  • La segunda restricción es no guardar en cookies del navegador ni en el servidor información que pueda identificar al lector
  • La dirección IP se usa solo para determinar el país y, antes de almacenarla, se aplica un hash conjunto de la IP y la fecha
  • Las solicitudes posteriores a la misma página se comparan con el hash de dirección IP + fecha y, si están duplicadas, se descartan
  • Con este método, durante un día una dirección IP cuenta como una sola lectura por página
  • La IP original no se almacena, y el hash que incluye la fecha tiene el efecto de expirar por día
user_agent = httpagentparser.detect(self.request.META.get('HTTP_USER_AGENT', None))
if user_agent.get('bot', False):
    print('Bot traffic')
    return

ip_hash = hashlib.md5(f"{client_ip(self.request)}-{timezone.now().date()}".encode('utf-8')).hexdigest()
country = get_user_location(client_ip(self.request)).get('country_name', '')
device = user_agent.get('platform', {}).get('name', '')
browser = user_agent.get('browser', {}).get('name', '')
referrer = self.request.GET.get('ref', '')

if referrer:
    referrer = urlparse(referrer)
    referrer = '{uri.scheme}://{uri.netloc}/'.format(uri=referrer)

Hit.objects.get_or_create(
    post_id=self.pk,
    ip_address=ip_hash,
    referrer=referrer,
    country=country,
    device=device,
    browser=browser)
  • El hash de la IP se usa solo para evitar hits duplicados durante un día, y por defecto todas las vistas de página se tratan como únicas
  • Al final de cada día, una tarea en segundo plano borra el hash de los logs de hits para evitar conflictos con interpretaciones excesivamente estrictas del GDPR
  • Una desventaja de este método es que, si se lee desde varios dispositivos el mismo día con la misma IP, solo se registra una lectura
  • Bear considera que esos casos representan una pequeña parte del tráfico, y cree que este enfoque basado en CSS ofrece una cifra de lecturas más precisa, además de ser más limpio y simple que varios métodos de recolección de analítica

1 comentarios

 
GN⁺ 2023-11-03
Comentarios en Hacker News
  • Soy el autor. El hash de la dirección IP en este contexto solo sirve para evitar vistas duplicadas dentro del mismo día.
    La idea es que cada vista de página sea básicamente única, y al final de cada día una tarea del worker borra el hash mientras conserva la información de vistas. Agregué una aclaración al artículo para dejarlo más claro.

    • Me pregunto si consideraste realmente servir una pequeña imagen transparente con un encabezado de caché privado que expire a medianoche, de forma que no se almacene la IP en absoluto.
    • Si 10 usuarios en una VPN compartida en algún lugar del mundo usan la misma IP y entran al sitio, ¿los cuentas como una sola persona? Lo mismo podría pasar en una red corporativa; la IP es una mala métrica.
  • La primera vez que vi la idea de usar solicitudes generadas por CSS para analítica me pareció genial.
    Alguien en Twitter puso una cuadrícula invisible de rectángulos sobre la página, e hizo que al pasar el cursor por cada celda se cargara una imagen de fondo única, para usarlo como seguimiento del mouse. Cada imagen de fondo enviaba una solicitud específica al servidor, y el servidor la interpretaba.
    Por diversión, un verano extendí esa idea para hacer un “chat web asincrónico solo con CSS” sin JavaScript: https://github.com/kkuchta/css-only-chat

  • Decir que anonimizar la dirección IP haciendo hash solo de la fecha y la IP es puro teatro de seguridad.
    Los hashes criptográficos están diseñados para calcularse rápido. Con hashcat se pueden calcular 6 mil millones de hashes MD5 por segundo en una MacBook M1 Pro, y solo existen 4 mil millones de direcciones IPv4. Puedes hacer fuerza bruta sobre todo el rango y recuperar la IP, así que en la práctica es lo mismo que revertir el hash.
    Incluso si usas un hash seguro como SHA-256 en lugar de MD5 roto, el problema sigue siendo el mismo.

    • Más allá de que técnicamente recuperar una IP desde el hash sea trivial, las autoridades de protección de datos de la UE han sido muy claras en que aplicar hash a datos personales no es anonimización.
      Aunque hagas hash del nombre completo de alguien, después aún puedes responder la pregunta “¿este hash coincide con este nombre completo en particular?”. Poder responder esa pregunta significa que el proceso de anonimización es reversible.
    • Soy el autor. También lo dejé en otro comentario abajo, pero parece más relevante en este hilo.
      El hash de la dirección IP en este contexto solo sirve para evitar vistas duplicadas dentro del mismo día. La idea es que cada vista de página sea básicamente única, y al final de cada día una tarea del worker elimina los hashes de IP que ya no hacen falta.
    • Como referencia, el mismo problema apareció en una discusión sobre algo parecido que hizo Storybook en su telemetría[0], y sin ninguna optimización me tomó como dos horas calcular hashes con salt para todas las IPv4 en mi laptop de casa.
      [0] https://news.ycombinator.com/item?id=37596757
    • A los hashes hay que agregarles salt. Con salt puede estar bien; sin salt, no.
      Si el salt se conserva para siempre o se rota periódicamente es solo un detalle de implementación, y el punto clave al agregar salt a hashes para analítica es que el salt nunca salga del cliente.
      Por la explicación del artículo, parece que no hay salt. O tal vez se usa la fecha actual como si fuera salt, pero eso no es aleatorio, así que cualquiera que quiera saber “¿la IP x.y.z.w visitó en la fecha aa-mm-dd?” puede adivinarlo fácilmente.
      Desde la perspectiva de un atacante, este tipo de problema es fácil de evaluar. ¿Cómo haría para descubrir algo sobre una persona específica con los datos disponibles? Si no puede, probablemente sea relativamente seguro guardar esos datos.
    • ¿No podrían usar un salt secreto o rotativo? No aparece en el código de ejemplo, así que la observación parece válida. Aun así, con agregar una sola cosa ya podría quedar razonablemente seguro.
      Igual me preocupa que incluso este tipo de teatro de seguridad alcance para pasar varias leyes y regulaciones sobre privacidad.
  • Se ve ingenioso, pero body:hover probablemente deje fuera casi por completo a usuarios que solo usan teclado y a agentes de usuario sin dispositivo apuntador, o sea, usuarios de tecnologías de asistencia.
    Aunque ese grupo pueda ser minoritario, siempre es una muy mala señal ver que se les excluye de alguna forma.
    No sé si existe una manera con CSS básico de detectar con 100% de fiabilidad en todos los agentes de usuario que “una persona real está leyendo este artículo” y además enviar una solicitud HTTP; de hecho, lo dudo. Algunos quizá ni siquiera soporten CSS, o podrían tener desactivada la carga de imágenes decorativas desde CSS.
    Un selector moderno que podría ayudar es :root:focus-within, pero requiere que el usuario realmente ponga foco en un elemento interactivo, y no todos los agentes de usuario garantizan eso. También podrían servir las animaciones vinculadas al scroll más modernas como @scroll-timeline, pero aun así probablemente dejarían fuera a los lectores braille.

    • ¿Minoritario? ¿No afecta eso a más del 50% de los agentes de usuario, es decir, teléfonos y tablets que no soportan :hover? Claro, asumiendo que no tengan un mouse conectado.
    • Tal como está implementado, en dispositivos con apuntador depende de la posición del puntero. Se activa si el puntero está dentro del área central de 760 px, pero no si está fuera.
      Esa área corresponde al ancho de la columna de contenido más 20 px de padding a cada lado. Así que algunos usuarios de teclado sí quedarían registrados, mientras que algunos usuarios de mouse, especialmente quienes usan viewports grandes, no.
  • Que “no solo cosas malas como Google Analytics, sino también Fathom y Plausible, tienen dificultades para registrar actividad en navegadores con bloqueadores de anuncios” me parece que se debe a que básicamente intentan sobrevivir en un yermo tóxico
    Usuarios como nosotros ya estamos hartos de todo ese concepto, así que si la analítica con CSS se vuelve popular, creo que también surgirán intentos por esquivarla

    • ¿Por qué?
      Yo desactivé manualmente el bloqueo de Piwik/Matomo, Plausible y Fathom en uBlock. No considero dañino lo que rastrean ni cómo lo hacen. Y además le dan al operador del sitio información útil para “mejorar el servicio”
      Por ejemplo, Plausible recopila menos información sobre mí que los logs típicos de nginx o Apache. Desde la perspectiva de un bloguero, es importante ver si una publicación llegó a HN, si fue enlazada desde algún lado y qué contenido se considera valioso y cuál se ignora. Así puedes escribir sobre lo que la gente realmente quiere leer y difundirlo por canales donde realmente se puede dar a conocer
    • Meter el access.log del servidor web en un servicio de analítica no impide nada
      Más bien, como es prácticamente imposible filtrar todo el tráfico de bots mirando solo el user agent, los números pueden inflarse
    • Lo que a mí me parece un yermo tóxico en la web es toda clase de publicidad. El rastreo es un problema mucho más sutil, pero a largo plazo existe el perjuicio de que se pueda crear un gemelo digital sobre el que se experimente para encontrar la mejor forma de manipularme
      No sé cuánta gente realmente teme eso. Supongo que las reacciones van desde “sí, qué escalofriante” hasta “ni hablar, eso es pura ciencia ficción”
    • Me acordé de uMatrix, que incluso permitía bloquear la carga de CSS
    • Este método no es más difícil de bloquear que uno basado en JavaScript. Al final, solo se trata de bloquear solicitudes que van a ciertos patrones de URL
  • Esto se conoce desde hace décadas como un rastreador por píxel

    • También se usa en correos electrónicos. Cargar una imagen transparente de 1x1 es más confiable que disparar un evento hover, pero los bloqueadores de anuncios suelen bloquear esas imágenes con frecuencia
    • Sí, aunque con CSS tiene algunos aspectos interesantes. Si usas :hover, puedes filtrar bots que no usan un WebDriver completo, es decir, la mayoría de los bots
      En cierto sentido, quizá sería mejor cargar un archivo .css casi vacío con @import junto con supports. Los bloqueadores de anuncios detectan muy bien los píxeles de seguimiento transparentes de 1px, pero podrían bloquear menos los archivos .css para no romper el diseño. Aunque en ese caso se pierde la ventaja ingeniosa de :hover
  • Es una pregunta sinceramente curiosa, aunque me preocupa que suene despectiva. ¿Cuál es el objetivo de recopilar datos analíticos en un blog personal no comercial como Bearblog?

    • Puedo responder desde la perspectiva de alguien que bloguea de forma constante desde alrededor del 2000 y que siempre ha tenido mucho interés en las “estadísticas”
      La razón principal para interesarse por la analítica es ver si tus textos se están leyendo. En apariencia, y en parte, puede ser por vanidad, pero en realidad tiene que ver con la conexión entre quien escribe y quien lee. De verdad te da curiosidad saber a qué reaccionan los lectores, y quieres darles más de eso. Ese “eso” puede ser el tema, el tono o la longitud. Ayuda a ajustar el material para tu audiencia. Al final, yo puedo escribir sobre una docena de temas de veinticuatro maneras distintas. Claro que escribo sobre lo que me gusta, pero lo afino para que resuene mejor con mis lectores
      En ese sentido, la analítica también es una forma de conocer a tus lectores. En los blogs con más participación, la analítica me daba algo parecido a un retrato borroso del lector. No solo podía ver qué le gustaba, sino también cuándo le gustaba. Podía saber si leían a primera hora de la mañana, en la hora del almuerzo o tarde en la noche, y eso ayudaba a decidir si publicar en cierto momento o a tener más confianza en esa decisión. Claro, toda esa información era difusa, pero realmente ayudaba a conectar de forma más activa con los lectores
    • Por el bucle de retroalimentación. A diferencia de lo que mucha gente cree, la analítica no existe solo para anuncios o para vender datos, sino para analizar el desempeño del sitio y del contenido
      Claro que puede usarse para publicidad y puede abusarse de ella, pero si quieres retroalimentación sobre lo que haces, es indispensable
      Ya sea que tu sitio lo lean 12 personas o 12,000, puede que no tenga valor económico. Pero desde un punto de vista personal, está bien saber qué quiere leer la gente de ti, sentir que el tiempo que dedicaste a escribir valió la pena y, si quieres, ajustar el rumbo hacia lo más popular
    • ¿No será por curiosidad? Quiero saber si alguien está leyendo lo que escribí. También es útil saber en qué le interesa a la gente
      Incluso un bloguero personal puede querer ajustar su contenido a sus lectores. Está bien saber que un texto sobre cierto tema fue leído por 500 personas y que otro sobre un tema distinto solo lo leyeron 3
  • A principios de este año intenté hacer algo así, pero perdí la motivación mientras construía la UI web. Mi método no usa CSS, sino que simplemente carga una imagen falsa con etiquetas
    https://github.com/nolytics

  • ¿Por qué no obtener simplemente esta información desde el servidor HTTP?

    • En la entrada del blog lo explican así
      “Siempre existe la opción de parsear los logs del servidor y obtener una idea aproximada del tipo de tráfico que llega al servidor. Pero por lo general, todo el tráfico del servidor se ve igual. Técnicamente, los bots deberían tener un user agent que se identifique como bot, pero como intentan extraer información como si fueran una ‘persona’ usando un navegador, casi nunca se identifican así. En esencia, si usas solo los logs del servidor para analítica, vas a ver el tráfico distorsionado por la gran cantidad de rastreadores de motores de búsqueda, scrapers y ahora incluso parsers basados en GPT”
    • Se mezclan todos los bots
    • Si lo operas de forma serverless, es difícil
  • ¿Cómo se almacenan los datos de analítica?
    Supongamos que existe un sitio de comercio electrónico y hay productos que quieres vender. Además de la analítica, se decide registrar directamente algunos comportamientos, como visitar la página de detalle de un producto mientras se ha iniciado sesión. Entonces se quiere guardar cosas como el ID de usuario, el ID del producto y la marca de tiempo
    ¿Cómo debería almacenarse esto en la práctica? Ingenuamente, se pensó que bastaba con meterlo en una tabla. El DBA preguntó cuánto tiempo se necesitaban los datos, y la respuesta fue al menos un mes
    Entonces dijo que estaba bien, y probablemente dejó programado algún trabajo para mover a otra tabla los datos más antiguos
    En la práctica, ¿cómo se almacenan este tipo de logs y durante cuánto tiempo se conservan?

    • Si no es a una escala enorme, meterlo en una tabla de Postgres está completamente bien. Incluso si la escala es grande, se puede particionar la tabla por fecha u otro atributo adecuado para no tener que lidiar con índices gigantes
      Ya lo hice así antes, y ni siquiera fue necesario pensar en particionado hasta llegar a unos mil millones de filas. Aun así, conviene particionar antes de llegar a ese punto. Esa experiencia no fue agradable
    • Una base de datos analítica es mejor. Cosas como ClickHouse o BigQuery
      Permiten hacer agregaciones mucho más rápido y manejan bien muchas columnas dispersas. Por ejemplo, un evento paid tiene la propiedad amount, mientras que un evento page_view tiene la propiedad url
    • Tengo 13 años de datos guardados en MySQL. La escala es de 5 millones de visitantes al año. Consultar ahí es doloroso, así que también mantengo una copia en ClickHouse. ClickHouse es realmente cómodo para hacer consultas
    • Uso Postgres y TimescaleDB. Si el sitio de comercio electrónico no es del tamaño de amazon.com, funciona bien
      Lo bueno de TimescaleDB es que se encarga de crear vistas materializadas para agregaciones de interés, como las vistas de productos por hora. Si hay tantos eventos que quieres evitar que la base de datos crezca demasiado, también puedes optar por “descartar” los eventos en sí y quedarte solo con las agregaciones
    • ClickHouse