Saltar al contenido principal
de/blog/obscura/research/web-fingerprinting/

obscura/research/web-fingerprinting

7 Min. Lesezeit

Übersicht

Signale, die von Webplattform-Features, CSS-Funktionen, Schriften und anderen Browser-Charakteristiken abgeleitet werden.


1. Schriftarten-Fingerprinting

Mechanismus

Schriften werden über verschiedene Techniken aufgelistet:

  1. CSS @font-face-Messung: Text erstellen, seine Breite mit einer bekannten Schrift messen, dann versuchen, eine Systemschrift zu laden. Ändert sich die Breite, existiert die Schrift.

  2. Canvas measureText: Ähnlicher Ansatz mit Canvas.

  3. document.fonts.check(): Moderne API zur Prüfung der Schriftverfügbarkeit.


  4. Flash-basierte Aufzählung (Legacy, noch verwendet): Flash kann alle installierten Schriften auflisten.


Code-Beispiel

// Messungsbasierte Schrifterkennung
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
const text = 'mmmmmmmmmmlli'

function detectFont(font) {
  ctx.font = `72px ${font}, monospace`
  return ctx.measureText(text).width
}

// Breite mit monospace-Baseline vergleichen, um zu erkennen

Entropie

Die Schriftliste variiert stark:

Plattform Typische Schriften Einzigartigkeit
Windows 11 Standard ~80 Schriften Mittel
macOS Ventura+ ~120 Schriften Mittel
Linux (Distribution variiert) 10-500 Schriften Sehr hoch
Benutzerdefinierte Schriften installiert Beliebig Sehr hoch

Abschwächung auf Proxy-Ebene

// Schriftmessung überschreiben, um konsistente Werte zurückzugeben
const originalMeasureText = CanvasRenderingContext2D.prototype.measureText
CanvasRenderingContext2D.prototype.measureText = function(text) {
  const result = originalMeasureText.call(this, text)
  // Breite runden, um Schrifterkennungspräzision zu reduzieren
  result.width = Math.round(result.width / 10) * 10
  return result
}

Einschränkung: Die Schrifterkennung wird nur reduziert, nicht beseitigt.


2. Browser-Feature-Unterstützung (25+ Binärvektoren)

Mechanismus

Jeder Browser unterstützt eine andere Teilmenge von Web-APIs und CSS-Features. Zusammen ergibt dies einen Browser+Versions-Fingerprint.

High-Entropie-Features

Feature Chrome Firefox Safari
WebGL Ja Ja Ja
WebGPU Ja (Flag) Nein Nein
WebUSB Ja Nein Nein
Web Bluetooth Ja Nein Nein
Web NFC Android Nein Nein
Web Authentication Ja Ja Ja
Web Assembly Ja Ja Ja
Service Workers Ja Ja Ja
Battery API Nein Ja Nein
Speech Synthesis Ja Ja Ja
GamePad API Ja Ja Ja (teilweise)
WebRTC Ja Ja Ja
CSS Grid Ja Ja Ja
CSS backdrop-filter Ja Ja Ja
CSS Container Queries Ja Ja Ja

Entropie

Jedes Feature fügt ~1 Bit hinzu. Bei ~100 getesteten Features beträgt das theoretische Maximum 100 Bits, aber Features sind stark korreliert (minimale Entropie über Browser-Familie und -Version hinaus).

Effektive Entropie: ~8-12 Bits (~256-4096 Kombinationen).

Abschwächung

Kann auf Proxy-Ebene nicht durchgeführt werden – dies sind inhärente Browser-Funktionen. Die Feature-Erkennung läuft in JS, bevor ein injiziertes Skript ausgeführt werden kann (und selbst wenn überschrieben, meldet der Browser die tatsächlichen Fähigkeiten irgendwo).


3. CSS-Media-Query-Fingerprinting

Mechanismus

CSS-@media-Queries können Bildschirmabmessungen, DPI, Farbtiefe und andere Eigenschaften ohne JS ermitteln:

@media (width: 1920px) { body::after { content: '1920' } }
@media (height: 1080px) { body::after { content: '1080' } }
@media (resolution: 1.5dppx) { body::after { content: '1.5' } }
@media (prefers-color-scheme: dark) { ... }
@media (prefers-reduced-motion: no-preference) { ... }
@media (hover: hover) { ... }
@media (pointer: fine) { ... }

Herausforderung für den Proxy

CSS wird vor jeder JS-Injection ausgeführt. Der Proxy kann:

  1. CSS in der Antwort-HTML parsen und Media Queries modifizieren
  2. Oder sich darauf verlassen, dass reine CSS-Fingerprint-Sonden Daten exfiltrieren müssen (was JS oder eine Netzwerkanfrage benötigt)

Echte Abschwächung: Den Exfiltrierungspfad blockieren (JS oder Bild laden) anstatt CSS-Werte zu spoofen.

Media-Query-Liste

Abfrage Werte Preisgegeben
width px Bildschirmbreite
height px Bildschirmhöhe
aspect-ratio Verhältnis Bildschirmverhältnis
resolution dppx Pixeldichte
color Bits Farbtiefe
color-gamut srgb/p3/rec2020 Farbraum
prefers-color-scheme light/dark OS-Design
prefers-reduced-motion no-preference/reduce OS-Einstellung
prefers-contrast no-preference/more/less OS-Einstellung
prefers-reduced-transparency no-preference/reduce OS-Einstellung
hover none/hover Eingabegerät
pointer none/coarse/fine Eingabegerät
any-hover none/hover Beliebiges Eingabegerät
any-pointer none/coarse/fine Beliebiges Eingabegerät
display-mode fullscreen/standalone PWA-Modus
prefers-color-gamut srgb/p3 Anzeigefarbraum
dynamic-range standard/high HDR-Unterstützung
inverted-colors none/inverted OS-Barrierefreiheit
monochrome Bits Monochrom-Anzeige
forced-colors none/active Barrierefreiheit
prefers-reduced-data no-preference/reduce Datensparmodus

4. ClientRects-Fingerprinting

Mechanismus

Element.getClientRects() gibt je nach OS, Browser und Schriftdarstellung leicht unterschiedliche Werte zurück:

const span = document.createElement('span')
span.textContent = 'mmmmmmmmmmlli'
document.body.appendChild(span)
const rects = span.getClientRects()
// rects[0].width variiert je nach OS/Browser/Schrift

Abschwächung

Element.prototype.getClientRects = function() {
  const rects = Element.prototype.__proto__.getClientRects.call(this)
  return Array.from(rects).map(r => ({
    ...r,
    width: Math.round(r.width / 10) * 10,
    height: Math.round(r.height / 10) * 10
  }))
}

5. Web Audio API-Fingerprint

Siehe audio-fingerprinting.md für Details.


6. WebGPU-Fingerprinting

WebGPU bietet strukturierte Geräteinformationen, die bisher nicht verfügbar waren:

const adapter = await navigator.gpu.requestAdapter()
const info = await adapter.requestAdapterInfo()
// { vendor: "nvidia", architecture: "ampere", device: "..." }

// Adapter-Limits legen spezifische GPU-Funktionen offen
const limits = adapter.limits
// maxTextureDimension2D, maxStorageBufferBindingSize, etc.

// Feature-Set
adapter.features.has('texture-compression-bc')
adapter.features.has('shader-f16')

Preisgegebene Daten

Feld Beispiel Identifizierbarkeit
vendor "nvidia", "apple", "amd", "intel" Niedrig (4-5 Werte)
architecture "ampere", "rdna2", "tigerlake" Mittel
device "integrated-graphics", "Apple M3" Mittel
driver "460.32.03" Hoch
backend "Metal", "Vulkan", "D3D12" Niedrig
type "discrete-gpu", "integrated-gpu", "cpu" Niedrig
memoryHeaps Größe und Anzahl Hoch
limits 50+ Fähigkeitswerte Sehr hoch

Abschwächung

// WebGPU-Adapterinfo blockieren
navigator.gpu.requestAdapter = async function() {
  return null  // oder einen gefälschten minimalen Adapter zurückgeben
}

Kosten: Blockiert WebGPU vollständig, was einige Apps bricht.


7. Service Worker-Persistenz

Mechanismus

Service Workers können verwendet werden, um Benutzer über Sitzungen hinweg zu identifizieren:

// Erkennen, ob ein SW installiert ist
navigator.serviceWorker.getRegistrations().then(regs => {
  if (regs.length > 0) console.log('SW gefunden')
})

Abschwächung

navigator.serviceWorker.getRegistrations =
  async () => []

8. WebGL-Erweiterungs-Fingerprint

WebGL gibt eine Liste unterstützter Erweiterungen preis, die je nach GPU, Treiber und Browser variiert:

Erweiterungstyp Anzahl Beispiel
Standard WebGL ~30 OES_texture_float
WEBGL_* ~15 WEBGL_debug_renderer_info
EXT_* ~15 EXT_texture_filter_anisotropic
Browserspezifisch ~5 MOZ_WEBGL_*

Entropie: Die genaue Menge und Anzahl der Erweiterungen erzeugt einen Hash.

Abschwächung:

const originalGetExtension = WebGLRenderingContext.prototype.getExtension
WebGLRenderingContext.prototype.getExtension = function(name) {
  if (name === 'WEBGL_debug_renderer_info') return null
  if (name.startsWith('MOZ_')) return null
  return originalGetExtension.call(this, name)
}

Schlussfolgerungen für Obscura

Vektor Proxy-Kontrolle Abschwächungsstrategie
Schriftaufzählung Teilweise Messungen runden, Flash blockieren
Feature-Unterstützung Keine Akzeptieren – dem Browser inhärent
CSS-Media-Queries Teilweise Exfiltration blockieren (DNS/Netzwerk)
ClientRects Teilweise Werte per JS-Injection runden
WebGPU Teilweise Adapterinfo blockieren
Service Workers Teilweise Registrierungs-APIs überschreiben
WebGL-Erweiterungen Teilweise Erweiterungsliste filtern