Canvas fingerprinting voorbij de basis: waarom twee identieke Chromes verschillende pixels renderen
Twee apparaten met dezelfde Chrome, OS en GPU renderen toch verschillende canvas-pixels. Waarom de rendering-pijplijn niet-deterministisch is, waarom noise-injectie averechts werkt en waar canvas past in een moderne detectiestack.
Canvas fingerprinting wordt sinds 2012 in productie gebruikt. Het idee is eenvoudig: vraag de browser om 2D-content te renderen, lees de pixels terug en hash ze. Verschillende apparaten produceren voor dezelfde instructies iets andere pixels, en die verschillen zijn stabiel genoeg om een apparaat over sessies heen te identificeren.
Dat is algemeen bekend. Wat minder goed wordt begrepen, is waarom de techniek werkt — waarom identieke softwareconfiguraties op identieke hardware toch verschillende pixeloutput produceren. Het antwoord ligt op het snijvlak van GPU-drivers, font-rendering en floating-pointrekenkunde.
De rendering-pijplijn is niet deterministisch
Wanneer JavaScript context.fillText("Cwm fjord bank glyphs vext quiz", 4, 45) aanroept, rendert Chrome de pixels niet zelf. Het genereert instructies voor Skia — de 2D-grafische bibliotheek — die op zijn beurt tekencommando's stuurt naar een GPU-versnelde backend (meestal ANGLE op Windows, Metal op macOS, of een software-fallback in containers).
Bij elke stap stapelen kleine implementatieverschillen zich op:
-
Het font-subsysteem van Skia selecteert een lettertypefamilie. Als Arial niet is geïnstalleerd, valt het terug — en die fallback varieert naargelang de op het systeem geïnstalleerde fonts.
-
Het geselecteerde font wordt op de gevraagde grootte gerenderd met FreeType (op Linux), DirectWrite (op Windows) of Core Text (op macOS). Elke bibliotheek heeft andere hinting-algoritmes.
-
Subpixelpositionering wordt toegepast. Hier beginnen identieke fonts verschillende pixels te produceren — het algoritme rondt posities af naar fractionele pixeloffsets, en de afrondingsregels verschillen per platform.
-
De gerasteriseerde tekst wordt op het canvas gecomposit. Blending gebeurt met GPU-shaders op hardware-versnelde systemen, en met CPU-code in de fallbackmodus.
Elk van deze stappen kan verschillen van één of twee pixelwaarden introduceren. Afzonderlijk onzichtbaar. Gezamenlijk uniek.
GPU-driverversies domineren de fingerprint
Twee Windows 11-machines met Intel UHD Graphics 620-chips kunnen verschillende canvas-output produceren als hun GPU-drivers verschillen. Intel brengt meerdere keren per jaar driverupdates uit, en elke update kan subpixelfiltering, gammacorrectie en tekst-anti-aliasing veranderen.
Dit betekent dat de stabiliteit van een canvas-fingerprint wordt begrensd door de frequentie van driverupdates. Een gebruiker die zijn grafische driver upgradet — vaak stilzwijgend, via Windows Update — ziet zijn canvas-fingerprint veranderen.
Detectiesystemen ondervangen dit door de canvas-fingerprint als één van vele signalen te behandelen, niet als losstaande identifier. Wanneer die verandert maar andere signalen stabiel blijven (TLS-fingerprint, WebGL-renderer, tijdzone, geïnstalleerde fonts), wordt de bezoeker nog steeds herkend.
Emoji-rendering is een goudmijn
Het meest identificerende element van een canvas-fingerprint is vaak geen tekst of vorm, maar emoji. Emoji-rendering hangt af van het emoji-font dat met het OS wordt meegeleverd — Segoe UI Emoji op Windows, Apple Color Emoji op macOS, Noto Color Emoji op Android.
Zelfs binnen één OS verandert emoji-rendering tussen versies. De weergave van een regenboog-emoji op Windows 10 verschilt van die op Windows 11. De grijnzende smiley van iOS 16 heeft andere anti-aliasing dan die van iOS 17.
Moderne canvas-fingerprintingscripts renderen specifiek reeksen emoji, inclusief nieuwe toevoegingen aan Unicode. Een bezoeker die beweert iPhone Safari te draaien maar een emoji die in iOS 17 is toegevoegd niet kan renderen, draait een oudere iOS — of liegt over het platform.
Automatiseringsframeworks produceren onderscheidende fingerprints
Headless Chrome, draaiend in een Docker-container zonder GPU, gebruikt SwiftShader voor rendering. SwiftShader produceert canvas-output die intern consistent is, maar afwijkt van elke hardware-versnelde Chrome.
De signatuur is herkenbaar: ongewoon schone anti-aliasing, specifieke kleurwaarden in gradiëntgebieden en tekstranden die bij geen enkele bekende GPU-driver passen. Een canvas-fingerprint die overeenkomt met SwiftShader in Chrome 124 in een Linux-container is vrijwel zeker automatisering — geen echte gebruiker draait een browser op deze manier.
Puppeteer-extra-stealth probeert canvas-output te spoofen door toDataURL() te onderscheppen en gewijzigde data terug te geven. Maar die wijziging introduceert vaak eigen artefacten — ruispatronen die zich over sessies heen herhalen, wat de anti-fingerprintinginspanning paradoxaal genoeg beter detecteerbaar maakt dan niets doen.
Waarom noise-injectie niet goed werkt
Privacygerichte browsers zoals Brave injecteren ruis in de canvas-output om tracking te voorkomen. Het idee is om kleine willekeurige variaties aan pixelwaarden toe te voegen, zodat hetzelfde apparaat elke sessie een andere fingerprint produceert.
In de praktijk heeft noise-injectie drie problemen:
1. De ruis is zelf een fingerprint. Een bezoeker wiens canvas-output bij elke lading verandert, maar wiens andere signalen identiek blijven, is herkenbaar als een noise-injecterende browser. Dat is op zichzelf al identificerend.
2. Het ruis-algoritme is stabiel. De implementatie van Brave produceert specifieke ruispatronen die niet overeenkomen met de natuurlijke variantie van GPU-rendering. Detectiesystemen kunnen canvas-output die noise-geïnjecteerd oogt onderscheiden van die er natuurlijk uitziet.
3. Geavanceerde systemen combineren samples. Meerdere renders op dezelfde pagina, of over sessies heen, kunnen worden gemiddeld. Als de ruis klein is, convergeert het gemiddelde naar de onderliggende deterministische fingerprint.
Wat canvas fingerprinting vastlegt
Naast de voor de hand liggende visuele output kan canvas fingerprinting als bijeffect verschillende apparaatkenmerken extraheren:
Rendertijd — hoe lang het canvas erover doet om te renderen, verraadt de GPU-capaciteit. Een 4K-desktop met een discrete GPU rendert sneller dan een midrange-laptop.
Tekstmetriek — measureText() geeft exacte pixelbreedtes terug die variëren per font-rendering. Tekstbreedte kan als fingerprint worden gebruikt, zelfs zonder te renderen.
Ondersteuning voor composite-operaties — sommige browsers of GPU-configuraties ondersteunen uitgebreide composite-modi. Testen op ondersteuning onthult de grenzen van de mogelijkheden.
Filtereffecten — CSS-achtige filters die op canvas worden toegepast, produceren verschillende output op verschillende GPU's, met name blur- en drop-shadow-filters.
Waar canvas fingerprinting past in een detectiestack
Op zichzelf is canvas fingerprinting geen robuuste identifier. Drivers veranderen. Gebruikers wisselen van browser. Noise-injectie komt vaak voor bij privacygerichte doelgroepen.
De echte kracht ligt in het gebruik als kruiscontrole. Wanneer een sessie op basis van cookies of accountlogin beweert een terugkerende gebruiker te zijn, fungeert de canvas-fingerprint als tweede signaal — overeenkomst met de historische fingerprint bevestigt de claim, een mismatch markeert account takeover of session hijacking.
Voor bot detection blinkt canvas fingerprinting uit in het betrappen van gecontaineriseerde automatisering. Echte gebruikers hebben echte GPU's. Echte GPU's produceren herkenbare rendersignaturen. Alles dat rendert als SwiftShader, of als een slecht gespoofd canvas, valt in de emmer vrijwel zeker bot.
De techniek uit 2012 werkt in 2026 nog om dezelfde reden: rendering is deterministisch per omgeving, maar bijna onmogelijk volledig te spoofen. Elke poging om zich te verbergen laat sporen van de poging zelf achter.