Übersicht
Canvas-Fingerprinting nutzt subtile Unterschiede in der Art und Weise aus, wie Hardware und Software Grafiken rendern. Diese Unterschiede entstehen durch:
- GPU-Modell und Treiberversion
- Betriebssystem-Schriftdarstellung (Anti-Aliasing, Hinting, Subpixel-Rendering)
- Browser-Rendering-Engine
- Installierte Systemschriften
Canvas-Fingerprinting
Mechanismus
- Eine Website zeichnet Formen und Text auf ein HTML5-
<canvas>-Element - Ruft
canvas.toDataURL()auf, um Pixeldaten als base64 zu extrahieren - Hasht das Ergebnis (MD5, SHA-1 oder benutzerdefinierter Hash)
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) // einzigartig pro Gerät
Entropiequellen
| Quelle | Variabilität |
|---|---|
| Schriftdarstellung (Anti-Aliasing, Hinting) | Hoch |
| Subpixel-Rendering-Reihenfolge (RGB vs. BGR) | Mittel |
| GPU-Rasterisierung von Formen | Mittel |
| Farbraumkonvertierung | Niedrig |
WebGL-Fingerprinting
Mechanismus
WebGL gibt die GPU direkt über Folgendes preis:
const gl = canvas.getContext('webgl')
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info')
const renderer = debugInfo.UNMASKED_RENDERER_WEBGL
// z. B. "NVIDIA GeForce RTX 3080/PCIe/SSE2"
const vendor = debugInfo.UNMASKED_VENDOR_WEBGL
// z. B. "NVIDIA Corporation"
Zusätzlich erzeugt das Rendern von 3D-Szenen hardwarespezifische Pixelausgaben.
Signale
UNMASKED_RENDERER_WEBGL– genaues GPU-ModellUNMASKED_VENDOR_WEBGL– GPU-Hersteller- WebGL-Shader-Präzision
MAX_TEXTURE_SIZE,MAX_VERTEX_ATTRIBS, etc.- Rendering einer 3D-Szene (Hash der Pixel)
WebGPU-Fingerprinting
WebGPU (der Nachfolger von WebGL) gibt noch detailliertere Informationen preis:
const adapter = await navigator.gpu.requestAdapter()
const info = await adapter.requestAdapterInfo()
// { vendor: "nvidia", architecture: "ampere", device: "..." }
Signale
adapterInfo.vendoradapterInfo.architectureadapterInfo.device- Feature-Set (welche GPU-Features unterstützt werden)
Warum Spoofing auf Proxy-Ebene schwierig ist
Canvas-/WebGL-/WebGPU-Rendering findet auf der lokalen GPU statt. Der Proxy sieht nur den Netzwerkverkehr. Bis die Daten den Proxy erreichen, ist das Rendering bereits abgeschlossen.
Mögliche Abschwächungsansätze:
API blockieren: WebGL null zurückgeben lassen, Canvas einen Fehler werfen lassen. Problem: erkennbar als Signal ("WebGL blockiert" ist selbst ein Fingerprint-Merkmal).
-
JS-Rauschen injizieren: CanvasBlocker (Firefox) randomisiert die Canvas-Ausgabe, indem vor
toDataURL()ein zufälliges Pixel injiziert wird. Problem: Wenn die Website Canvas 3-mal liest und 3 verschiedene Hashes erhält, erkennt sie Manipulation. Erfordert zudem frequenzkonsistentes Rauschen. -
Image-Data-Override via Proxy:
CanvasRenderingContext2D.prototype.getImageDataper injiziertem JS überschreiben, um generische Daten zurückzugeben. Problem: funktioniert nur mitgetImageData, nicht mittoDataURL. Mehrere Wege, um Pixel zu extrahieren.
Software-GPU-Rendering (SwiftShader/Lavapipe): GPU-Rendering durch CPU-basierten deterministischen Renderer ersetzen. Problem: massive Leistungseinbußen, keine 3D-Beschleunigung.
Formeln
Canvas-Fingerprint-Entropie
Wobei die Wahrscheinlichkeit eines gegebenen Hashes über verschiedene Geräte hinweg ist.
Geschätzte Entropie: ~5-7 Bits (kann ~32-128 Geräte unterscheiden).
Einzigartigkeits-Wahrscheinlichkeit
Wobei die Bevölkerungsgröße und die Anzahl der unterschiedlichen Canvas-Ausgaben in der Stichprobe ist.
Schlussfolgerungen
- Canvas-/WebGL-Fingerprinting kann auf Proxy-Ebene nicht vollständig gespooft werden
- Rausch-Injektion per JS ist eine teilweise Abschwächung, aber erkennbar
- Das Blockieren dieser APIs ist erkennbar und wird selbst zu einem Signal
- Die einzige nahezu vollständige Lösung ist der Ansatz des Tor-Browsers (alle Benutzer auf identische Ausgabe normalisieren)
- Für Obscura: Bester Ansatz ist injiziertes JS, das konsistentes Rauschen hinzufügt +
WEBGL_debug_renderer_infoblockiert