- Bear Blog hat wegen Einschränkungen bei Geschwindigkeit, Effizienz und Stabilität ein eigenes Analytics-System implementiert, das ohne clientseitiges JavaScript auskommt
- Gängige Analytics-Skripte können von Adblockern blockiert werden, und reine Server-Logs können durch Crawler, Scraper und GPT-basierte Parser verzerrte Besuchsstatistiken liefern
- Wenn im CSS jeder Seite
body:hover ausgelöst wird, ruft border-image den Endpunkt /hit/{{ post.id }}/ auf und nutzt Hover bzw. mobiles Scrollen als Lesesignal
- Der Server prüft anhand des User-Agents, ob es sich um einen Bot handelt, sowie Browser und Plattform; die IP-Adresse wird nur zur Länderbestimmung genutzt und anschließend per IP+Datum-Hash zur Deduplizierung von Lesevorgängen verwendet
- Liest dieselbe IP am selben Tag über mehrere Geräte, wird dies als ein einziger Lesevorgang gezählt; zugleich lassen sich ohne Speicherung identifizierender Informationen einfach Unique Reads pro Seite erfassen
Lese-Events per CSS-Hover erzeugen
- Das Analytics-System von Bear Blog folgt der Einschränkung, clientseitig kein JavaScript zu verwenden
- Gängige Analytics-Tools können mit clientseitigem JavaScript teilweise einschätzen, ob Traffic echt ist oder von Bots stammt; viele Adblocker blockieren jedoch nicht nur Google Analytics, sondern auch Analytics-Skripte wie Fathom oder Plausible
- Allein das Parsen von Server-Logs kann eine verzerrte Sicht erzeugen, weil Suchmaschinen-Crawler, Scraper und GPT-basierte Parser wie normaler Traffic darin auftauchen
- Bear fügt jeder Seite das folgende CSS hinzu, sodass
body:hover ausgelöst wird, wenn Nutzer den Cursor über die Seite bewegen oder auf Mobilgeräten scrollen
body:hover {
border-image: url("/hit/{{ post.id }}/?ref={{ request.META.HTTP_REFERER }}");
}
- Wenn
body:hover getriggert wird, wird die Hit-URL des jeweiligen Beitrags aufgerufen
- Die Information, die dieser Anfrage explizit erneut hinzugefügt wird, ist der Referrer; die Schreibweise
HTTP_REFERER ist eine falsch geschriebene Form, die wie ein Standard erhalten geblieben ist
- Bear nutzt den Umstand, dass Bots nicht hovern, und wertet den Aufruf auf Basis von
body:hover als Signal für menschliche Leser
- Anschließend prüft der Server, ob der User-Agent ein Bot ist, und extrahiert Browser und Plattform aus dem User-Agent-String
Deduplizierung von Lesevorgängen ohne identifizierende Informationen
- Die zweite Einschränkung besteht darin, keine Informationen, mit denen Leser identifiziert werden können, in Browser-Cookies oder auf dem Server zu speichern
- Die IP-Adresse wird nur zur Bestimmung des Landes verwendet; vor dem Speichern werden IP-Adresse und Datum gemeinsam gehasht
- Nachfolgende Anfragen an dieselbe Seite werden mit dem Hash aus
IP-Adresse + Datum verglichen und verworfen, wenn sie Duplikate sind
- Bei diesem Verfahren zählt eine IP-Adresse innerhalb eines Tages für eine Seite als ein einziger Read
- Die IP-Adresse im Klartext wird nicht gespeichert, und der Hash mit enthaltenem Datum hat faktisch eine Ablaufwirkung von einem Tag
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)
- Der IP-Hash dient nur dazu, doppelte Hits innerhalb eines Tages zu verhindern; alle Seitenaufrufe werden grundsätzlich als eindeutig behandelt
- Am Ende jedes Tages entfernt ein Hintergrundjob die Hashes aus den Hit-Logs, um nicht mit übermäßig strengen Auslegungen der DSGVO in Konflikt zu geraten
- Ein Nachteil dieses Ansatzes ist, dass mehrere Geräte hinter derselben IP-Adresse am selben Tag nur als ein einziger Read gezählt werden
- Bear geht davon aus, dass solche Fälle nur einen kleinen Teil des Traffics ausmachen, und sieht den CSS-basierten Ansatz im Vergleich zu mehreren anderen Analytics-Erfassungsmethoden als knapper, einfacher und dennoch als Quelle genauer Lesezahlen an
1 Kommentare
Meinungen auf Hacker News
Ich bin der Autor. Der Hash der IP-Adresse dient in diesem Kontext nur dazu, doppelte Aufrufe innerhalb eines Tages zu verhindern.
Er soll im Grunde jeden Seitenaufruf eindeutig machen, und am Ende jedes Tages leert ein Worker-Job den Hash, während die Aufrufinformationen erhalten bleiben. Ich habe dem Artikel eine Änderung hinzugefügt, um das klarzustellen.
Als ich die Idee zum ersten Mal sah, CSS-ausgelöste Requests für Analytics zu nutzen, fand ich sie wirklich großartig.
Jemand auf Twitter legte ein unsichtbares Raster aus Rechtecken über die Seite und ließ beim Hover über jede Zelle ein eindeutiges Hintergrundbild laden, um damit Maus-Tracking zu machen. Jedes Hintergrundbild schickt einen bestimmten Request an den Server, den der Server dann interpretiert.
Aus Spaß habe ich diese Idee in einem Sommer erweitert und einen „CSS-only asynchronen Webchat“ ohne JavaScript gebaut: https://github.com/kkuchta/css-only-chat
Nur Datum und IP zu hashen und damit zu behaupten, die IP-Adresse zu anonymisieren, ist schlicht Security Theater.
Kryptografische Hashes sind darauf ausgelegt, schnell berechnet zu werden. Mit hashcat kann man auf einem MacBook M1 Pro 6 Milliarden MD5-Hashes pro Sekunde berechnen, und es gibt nur 4 Milliarden IPv4-Adressen. Man kann den gesamten Bereich per Brute Force durchprobieren und die IP-Adresse finden; praktisch ist das dasselbe, als würde man den Hash zurückrechnen.
Selbst wenn man statt des kaputten MD5 einen sicheren Hash wie SHA-256 verwendet, gilt das genauso.
Selbst wenn man den vollständigen Namen einer Person hasht, kann man später die Frage beantworten: „Entspricht dieser Hash diesem bestimmten vollständigen Namen?“ Dass man diese Frage beantworten kann, bedeutet, dass der Anonymisierungsprozess reversibel ist.
Der Hash der IP-Adresse dient in diesem Kontext nur dazu, doppelte Aufrufe innerhalb eines Tages zu verhindern. Er soll im Grunde jeden Seitenaufruf eindeutig machen, und am Ende jedes Tages entfernt ein Worker-Job die IP-Hashes, die dann nicht mehr benötigt werden.
[0] https://news.ycombinator.com/item?id=37596757
Ob man den Salt dauerhaft speichert oder regelmäßig rotiert, sind nur Implementierungsdetails; der Kernpunkt beim Salten von Analytics-Hashes ist, dass der Salt den Client niemals verlässt.
Nach der Beschreibung im Artikel sieht es so aus, als gäbe es keinen Salt. Oder es wird das aktuelle Datum wie ein Salt verwendet, aber das ist kein zufälliger Salt und lässt sich für jeden leicht erraten, der wissen will: „Hat IP x.y.z.w am Datum yy-mm-dd besucht?“
Aus Angreifersicht ist es leicht, solche Probleme zu beurteilen. Wie würde man mit den gegebenen Daten etwas über eine bestimmte Person herausfinden? Wenn das nicht geht, ist es im Allgemeinen wahrscheinlich okay, diese Daten zu speichern.
Ich mache mir allerdings Sorgen, dass schon solches Security Theater ausreichen könnte, um verschiedene Datenschutzgesetze und -vorschriften zu passieren.
Es wirkt zwar clever, aber
body:hoverwird reine Tastaturnutzer und User Agents ohne Zeigegerät, also Nutzer assistiver Technologien, wahrscheinlich fast vollständig verpassen.Auch wenn diese Gruppen randständig sein mögen, ist es immer ein sehr schlechtes Signal, wenn man sieht, dass sie in irgendeiner Weise ausgeschlossen werden.
Ich weiß nicht, ob es mit reinem CSS eine Möglichkeit gibt, über alle User Agents hinweg zu 100 % zuverlässig zu erkennen, dass „ein echter Nutzer diesen Artikel liest“, und einen HTTP-Request zu senden; ich bezweifle es auch. Manche unterstützen CSS gar nicht oder haben das Laden dekorativer CSS-Bilder deaktiviert.
Ein moderner Selektor, der helfen könnte, ist
:root:focus-within, aber der Nutzer muss tatsächlich ein interaktives Element fokussieren, und nicht jeder User Agent garantiert das. Auch das hochmoderne scrollgebundene Animationsfeature@scroll-timelinewäre denkbar, aber Braille-Reader würden wahrscheinlich weiterhin durchs Raster fallen.:hovernicht unterstützen, also mehr als 50 % der User Agents? Natürlich sofern keine Maus angeschlossen ist.Dieser Bereich ist die Breite der Content-Spalte plus 20px Padding auf beiden Seiten. Dadurch werden manche Tastaturnutzer erfasst und manche Mausnutzer, insbesondere mit großen Viewports, nicht erfasst.
Dass „nicht nur schlimme Dinge wie Google Analytics, sondern auch Fathom und Plausible in Browsern mit Adblockern Schwierigkeiten haben, Aktivitäten aufzuzeichnen“, liegt meiner Ansicht nach daran, dass sie im Grunde versuchen, in einer toxischen Ödnis zu überleben.
Nutzer wie wir haben von diesem ganzen Konzept genug, und wenn CSS-Analytics populär werden, wird es meiner Meinung nach auch Versuche geben, sie zu umgehen.
Ich habe Piwik/Matomo, Plausible und Fathom in uBlock manuell entsperrt. Ich sehe keinen Schaden darin, was und wie sie tracken. Und sie geben Seitenbetreibern nützliche Informationen zur „Verbesserung des Dienstes“.
Plausible sammelt zum Beispiel weniger Informationen über mich als normale nginx- oder Apache-Logs. Aus Sicht eines Bloggers ist es wichtig zu sehen, ob ein Beitrag auf HN gelandet ist, ob er irgendwo verlinkt wurde, welche Inhalte als wertvoll wahrgenommen und welche ignoriert werden. Nur so kann man Inhalte schreiben, die die Leute tatsächlich lesen wollen, und sie über Kanäle verbreiten, die man tatsächlich nachvollziehen kann.
access.logeines Webservers in einen Analytics-Dienst einzuspeisen, verhindert gar nichts.Im Gegenteil: Weil es praktisch unmöglich ist, allen Bot-Traffic nur anhand des User-Agents herauszufiltern, können die Zahlen aufgebläht werden.
Ich weiß nicht, wie viele Menschen davor tatsächlich Angst haben. Die Reaktionen dürften von „ja, gruselig“ bis „Unsinn, das ist nur Science-Fiction“ reichen.
Das ist seit Jahrzehnten als Tracking-Pixel bekannt.
:hoverkann man Bots herausfiltern, die keinen vollständigen WebDriver verwenden, also die meisten Bots.In gewisser Weise könnte es sogar besser sein, eine nahezu leere
.css-Datei zusammen mitsupportsper@importzu laden. Adblocker erkennen transparente 1px-Tracking-Pixel sehr gut, werden.css-Dateien aber wahrscheinlich seltener blockieren, um Layouts nicht zu zerstören. In diesem Fall ginge allerdings der clevere Vorteil von:hoververloren.Eine ernst gemeinte Frage, auch wenn ich befürchte, dass sie abwertend klingt: Was ist das Ziel beim Sammeln von Analytics-Daten auf einem nichtkommerziellen persönlichen Blog, wie es Bearblog zu sein scheint?
Der Hauptgrund für mein Interesse an Analytics ist zu sehen, ob meine Texte gelesen werden. Oberflächlich, und in gewisser Weise, geht es um Eitelkeit, aber tatsächlich geht es um die Verbindung zwischen Autor und Lesern. Ich bin wirklich neugierig, worauf Leser reagieren, und möchte ihnen mehr davon geben. Dieses „davon“ kann das Thema sein, der Tonfall oder die Länge. Es hilft mir, Stoffe auf meine Leserschaft zuzuschneiden. Letztlich kann ich über ein Dutzend Themen auf zwei Dutzend Arten schreiben. Natürlich schreibe ich über das, was mir gefällt, aber ich schleife es so, dass es bei den Lesern besser ankommt.
In diesem Sinne sind Analytics auch eine Möglichkeit, die Leser kennenzulernen. Bei Blogs mit hoher Beteiligung gaben mir Analytics so etwas wie ein unscharfes Porträt der Leserschaft. Ich konnte nicht nur sehen, was sie mögen, sondern auch wann sie es mögen. Ob sie gleich morgens lesen, in der Mittagspause oder spätabends; das half mir zu entscheiden, ob ich zu bestimmten Zeiten veröffentliche, oder gab mir Sicherheit bei dieser Entscheidung. Natürlich waren das alles unscharfe Informationen, aber sie halfen wirklich dabei, aktiver mit den Lesern in Verbindung zu treten.
Natürlich kann man sie für Werbung nutzen und missbrauchen, aber wenn ich Feedback zu dem haben möchte, was ich mache, sind sie unverzichtbar.
Ob 12 oder 12.000 Menschen eine Website lesen, kann finanziell wertlos sein. Aus persönlicher Sicht ist es aber gut zu wissen, was Menschen von mir lesen wollen; ich kann das Gefühl haben, dass die Zeit fürs Schreiben gut investiert war, und, wenn ich möchte, stärker in Richtung der beliebteren Inhalte nachjustieren.
Auch als persönlicher Blogger kann man Inhalte an die Leser anpassen wollen. Es ist gut zu wissen, dass ein Beitrag zu einem Thema von 500 Leuten gelesen wurde und ein Beitrag zu einem anderen Thema nur von 3.
Anfang des Jahres wollte ich so etwas ausprobieren, habe aber beim Bau der Web-UI die Motivation verloren. Mein Ansatz ist nicht CSS, sondern lädt einfach per Tag ein Fake-Bild.
https://github.com/nolytics
Warum holt man diese Informationen nicht einfach vom HTTP-Server?
„Es gibt immer die Möglichkeit, Server-Logs zu parsen, und man kann grob verstehen, welche Arten von Traffic auf den Server zugreifen. In der Regel sieht jedoch der gesamte Server-Traffic gleich aus. Technisch gesehen sollten Bots einen User-Agent haben, der sie als Bots ausweist, aber weil sie versuchen, Informationen wie ein ‚Mensch‘ mit Browser abzugreifen, kennzeichnen sie sich fast nie so. Im Kern führt die reine Analyse von Server-Logs wegen Suchmaschinen-Crawlern, Scrapern und inzwischen auch GPT-basierten Parsern dazu, dass der Traffic verzerrt dargestellt wird.“
Wie speichert man Analysedaten?
Nehmen wir an, es gibt eine E-Commerce-Website und Produkte, die verkauft werden sollen. Neben Analytics entscheidet man sich, bestimmte Aktionen direkt zu loggen, etwa den Besuch einer Produktdetailseite im eingeloggten Zustand. Daher möchte man Dinge wie Nutzer-ID, Produkt-ID und Zeitstempel speichern.
Wie sollte man das in der Praxis speichern? Naiv dachte ich, man könne es einfach in eine Tabelle schreiben. Der DBA fragte, wie lange die Daten benötigt würden, und ich antwortete: mindestens einen Monat. Darauf sagte er okay; vermutlich hat er einen Job eingerichtet, der ältere Daten in eine andere Tabelle verschiebt.
Wie speichert man solche Logs tatsächlich, und wie lange bewahrt man sie auf?
Ich habe das früher so gemacht und musste bis etwa 1 Milliarde Zeilen nicht einmal über Partitionierung nachdenken. Trotzdem ist es besser, früher zu partitionieren. Diese Erfahrung war nicht angenehm.
Aggregationen lassen sich damit viel schneller durchführen, und sie kommen gut mit vielen spärlich gefüllten Spalten zurecht. Zum Beispiel hat ein
paid-Event ein Attributamount, während einpage_view-Event ein Attributurlhat.Das Gute an TimescaleDB ist, dass es sich selbst um das Erstellen materialisierter Views für Aggregationen kümmert, die einen interessieren, etwa Produktaufrufe pro Stunde. Wenn man wegen vieler Events vermeiden möchte, dass die Datenbank zu groß wird, kann man auch entscheiden, die Events selbst „wegzuwerfen“ und nur die Aggregationen zu behalten.