Ü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:
-
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. -
Canvas
measureText: Ähnlicher Ansatz mit Canvas. -
document.fonts.check(): Moderne API zur Prüfung der Schriftverfügbarkeit.
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:
- CSS in der Antwort-HTML parsen und Media Queries modifizieren
- 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 |