Saltar al contenido principal
en/blog/obscura/research/uncontrollable/gpu-rendering/

obscura/research/uncontrollable/gpu-rendering

5 min read

Category: Hardware / Low-Level Rendering


1. Description

GPU fingerprinting exploits the fact that graphics rendering is not bit-identical across different hardware. The same JavaScript code produces different pixel outputs on different GPUs due to:

  • GPU microarchitecture (ALU count, rasterizer units, TMUs, ROPs)
  • Driver version and shader compiler optimizations
  • Floating-point precision in shader calculations
  • Anti-aliasing algorithms and their implementation
  • Subpixel rendering order (RGB vs BGR)
  • Color space conversion matrices
  • Texture compression support
  • Max texture size and other hardware limits

2. Vectors

2.1 Canvas Fingerprinting

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('Test string', 2, 15)
const hash = md5(canvas.toDataURL())

Entropy: ~5-7 bits (32-128 devices distinguishable)

Stability: High stable across browser restarts, moderate across browser updates

2.2 WebGL Fingerprinting

const gl = canvas.getContext('webgl')
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info')
// Exact GPU model:
const renderer = debugInfo.UNMASKED_RENDERER_WEBGL
// "NVIDIA GeForce RTX 3080/PCIe/SSE2"
const vendor = debugInfo.UNMASKED_VENDOR_WEBGL
// "NVIDIA Corporation"

Additional WebGL signals:

  • WebGL scene render hash (render a 3D scene, hash pixels)
  • Shader precision (highp, mediump, lowp support and precision)
  • MAX_TEXTURE_SIZE, MAX_VERTEX_ATTRIBS, MAX_VIEWPORT_DIMS (50+ numeric parameters)
  • WebGL extension list

Entropy: ~20-25 bits (renderer + extensions + limits)

Stability: Very High changes only with GPU driver updates

2.3 WebGPU Fingerprinting

const adapter = await navigator.gpu.requestAdapter()
const info = await adapter.requestAdapterInfo()
// { vendor: "nvidia", architecture: "ampere", device: "GA102", driver: "535.129.03", backend: "Vulkan" }
// adapter.limits contains 50+ numeric capabilities
// adapter.features contains ~30+ feature flags

Entropy: ~30+ bits (adapter info + limits + features)

Stability: Very High hardware-locked


3. Attacker's Strengths

Strength Explanation
Hardware-locked GPU fingerprint cannot change without hardware replacement
Cross-browser Same GPU produces similar fingerprints across Chrome, Firefox, Edge
Passive Requires no user interaction, runs in background
No network dependency Rendering is local, no request needed
Precise Exact GPU model is highly identifying (rare GPUs are very unique)
Consistent Same GPU driver version same render output every time

4. Attacker's Weaknesses

Weakness Explanation Exploitable?
Blockable WebGL/WebGPU can be disabled in browser settings Yes but detectable
Noise injection CanvasBlocker adds random pixels to canvas output, breaking fingerprint Yes but detectable
Driver updates New drivers change rendering output, invalidating stored fingerprints Occasional
Tor Browser Software rendering normalizes all GPU output Yes but massive performance cost
Identifies GPU, not user All users with same GPU model look identical Only for common GPUs

5. Detection of Tampering

Attackers can detect canvas/WebGL spoofing via:

Technique How It Works
Sequential read comparison Read canvas 2-3 times, compare hashes. Noise injection produces different hashes.
Statistical analysis Real hardware output has characteristic noise patterns; injected noise looks different
Cross-API correlation Canvas hash + WebGL renderer + AudioContext should all be consistent
Feature presence WEBGL_debug_renderer_info returning null is suspicious
WebGPU adapter info missing If WebGL works but WebGPU doesn't, that's unusual

6. Mitigations for Obscura

6.1 What Obscura Can Do

Mitigation Effectiveness Detectability Implementation
Block WEBGL_debug_renderer_info High (hides GPU model) Medium JS injection: return null
Block WebGPU adapter info High (hides GPU info) Medium JS injection: requestAdapter null
Canvas noise injection Medium High JS injection: CanvasBlocker algorithm
Block WebGL entirely Very High High JS injection: getContext null
Block WebGPU entirely Very High High JS injection: requestAdapter null
Round canvas dimensions Low Medium JS injection: Math.round()

6.2 What Obscura Cannot Do

Cannot Why
Change actual GPU rendering Rendering happens on local GPU hardware
Spoof WebGL scene render hash Rendering output is hardware-dependent
Spoof WebGPU limits Limits are queried from the physical adapter
Normalize canvas output across users Requires software GPU (SwiftShader) Tor Browser only

6.3 Recommended Approach

1. Block WEBGL_debug_renderer_info      (hides GPU model  high value, low cost)
2. Block WebGPU adapterInfo              (hides next-gen GPU info  high value, low cost)
3. Inject canvas noise                   (breaks canvas fingerprint  medium value, medium cost)
4. If device is powerful: suggest Tor    (complete protection  high value, high user cost)

7. Research References

  • Mowery, K. & Shacham, H. (2012). "Pixel Perfect: Fingerprinting Canvas in HTML5." WOOT 2012.
  • Acar, G. et al. (2014). "The Web Never Forgets: Persistent Tracking Mechanisms in the Wild." CCS 2014.
  • Cao, Y. et al. (2017). "WebRTC and Canvas-based Device Fingerprinting." PET Symposium 2017.
  • FingerprintJS (2026). "Canvas Fingerprinting: What It Is and How It Works." fingerprintjs.com/blog.