Fingerprinting is de nieuwe cookie

Vanaf nu noem ik je: ...

Probeer de pagina te vernieuwen. Wis de cookies. Maak verbinding via een VPN. Je bent nog steeds dezelfde ...

Open de pagina nu op je telefoon of een ander apparaat; je bijnaam zal anders zijn, maar net zo consistent. Is hij anders in de incognito-modus? Goed. Maar is hij altijd hetzelfde? Dat is slecht nieuws.

Stel je het volgende voor: de meeste websites die je bezoekt, worden betaald om advertenties te tonen en draaien indirect mijn script. Hiermee weet ik wie je bent. Ik kan dit gebruiken om te bepalen welke advertenties je ziet. Maar belangrijker: als je een account hebt op die website, kan ik aanbieden om je data te kopen — alles wat ze binnen hun privacybeleid kunnen delen: je naam, activiteiten, gesprekken met AI...

Al snel zou ik meer over je weten dan je vrienden, en misschien zelfs meer dan je zelf weet.

Je zou kunnen zeggen: "Wat maakt dat uit? Ik heb niets te verbergen. En ik heb liever relevante advertenties... soms tonen ze me precies wat ik nodig heb! Bovendien zijn de gegevens geanonimiseerd..."

Het punt is dat het helemaal niet om jou gaat. Het gaat inmiddels ook niet meer echt om de advertenties. Niemand geeft om jou als individu. En mochten de politie of de overheid wel geïnteresseerd zijn, dan neem ik hun geld ook graag aan.

Als ik naar al deze data kijk, zie ik geen mensen. Ik zie patronen. Patronen die voortkomen uit patronen. Een levende, digitale fractale staat van de economie, de samenleving en de menselijke psyche. De glazen bol van de 21e eeuw, gemaakt van silicium! Dat is het product. En dat gaat naar de hoogste bieder.

Wat maakt dat jou dan? Een tandwiel? Een os? Een vrijwilliger?

De verschuiving van cookies naar fingerprinting

Herinner je je de grote ophef over third-party cookies? Het was zo'n groot thema dat de EU al een van haar grootste uitvindingen had uitgerold: de toestemmingsbanners (consent banners).

Als de EU sneller handelt dan techbedrijven, kan dat alleen betekenen dat die bedrijven stilzitten. En een van die techbedrijven bezit toevallig tweederde van de browsermarkt. Wat was het plan? Laten we applaus geven voor Google — het grootste advertentiebedrijf op aarde!

Akte I: Druk

"Privacy is voor ons van het grootste belang in alles wat we doen."

Het is 2019 en cookies zijn nog steeds een actueel thema. Het is slecht voor de PR en browsers zoals Safari en Firefox blokkeren ze al. Een jaar later belooft Google: we zullen cookies vernietigen en een nieuw, privacy-gericht tijdperk inluiden.

Akte II: Afleiding

"In plaats van third-party cookies uit te faseren, introduceren we een nieuwe ervaring."

Vijf jaar later zijn mensen in beslag genomen door veel grotere zorgen dan cookies. De oplossingen die Google heeft ontwikkeld (Privacy Sandbox) werken matig en hebben geleid tot antitrust-zorgen, waardoor niemand ze gebruikt.

De conclusie: "We houden de cookies gewoon. We maskeren wel je IP-adres!" (Al gebeurde dit alleen in incognito-modus bij geauthenticeerde gebruikers, en uiteindelijk is dat zelfs niet volledig geleverd).

Akte III: Acceleratie

Nu kun je device fingerprinting gebruiken in onze advertentieproducten. Er is nu een betere manier om te weten wie wie is.

Hoe werkt fingerprinting?

Laten we hier dieper op ingaan. Wanneer je een website bezoekt, stuurt je browser een verzoek naar de server en de server stuurt code terug die de browser moet uitvoeren.

Het verzoek onthult al informatie over jou: welke browser je gebruikt, de taalinstelling, het besturingssysteem en je IP-adres (dat de server nodig heeft om de reactie te kunnen versturen). Dit laatste onthult vaak je locatie.

Daarna wordt de code uitgevoerd.

We hebben veel civiele vooruitgang geboekt in het veilig maken van browsen. De tijd dat je dagelijks bang moest zijn voor een virus door simpelweg een website te bezoeken, ligt ver achter ons. Echter, de definitie van "veiligheid" verschilt per persoon. De brede consensus stopt bij isolatie: de code van een website mag de gegevens die door andere websites zijn opgeslagen niet inzien, en mag zonder toestemming niet bij je bestanden of andere applicaties.

Chrome en Edge stoppen bij deze basisdefinitie. Safari (vooral op iOS/macOS 26+) en Firefox breiden die definitie uit naar privacy, maar beschermen je nog steeds niet volledig tegen fingerprinting. Brave en Tor zijn de leidende privacy-first browsers, samen met de opkomende Mullvad Browser.

De code wordt uitgevoerd op jouw hardware, die op manieren gemeten kan worden die consistente en unieke waarden opleveren. Hiertegen kan weinig gedaan worden, behalve via twee gevestigde oplossingen:

  1. Ruis toevoegen: Hier en daar wat "ruis" invoeren. Dit wordt farbling genoemd en is wat Brave doet om je fingerprint inconsistent te maken.
  2. Uniformiteit: Alles er altijd hetzelfde uit laten zien, zodat browsergebruikers niet van elkaar te onderscheiden zijn. Tor en Mullvad hanteren deze aanpak.

Hoe een fingerprint wordt gemaakt

Er zijn twee bronnen voor data die samen jouw fingerprint vormen: het netwerkverzoek en de code-omgeving.

Het netwerkverzoek

Data uit het netwerk is over het algemeen fragiel of generiek:

  • Het IP-adres: In een ideale situatie waarbij één persoon altijd hetzelfde IP-adres gebruikt, is dit wellicht de enige data die nodig is. Maar vaak browsen mensen via mobiele netwerken, delen ze wifi, gebruiken ze VPN's of roteert de provider het IP-adres. Het is dus fragiel.
  • Headers (zoals User-Agent of Accept-Language): Deze onthullen je browser, besturingssysteem, versies en de ingestelde taal.

De code-omgeving

Wanneer code op het apparaat kan worden uitgevoerd, zijn er manieren om hardware-eigenschappen te meten zonder dat er toestemming wordt gevraagd. Deze zijn verrassend stabiel:

  • GPU: Gedrag, fabrikant, driver.
  • CPU: Gedrag, fabrikant, aantal threads.
  • Geheugengrootte.
  • Schermgegevens: Resolutie, pixelratio, verversingssnelheid.
  • Audioverwerking.
  • Apparaatmodel.
  • Softwareniveau: Geïnstalleerde lettertypen, extensies, systeeminformatie.

Miljoenen apparaten gebruiken de nieuwste versie van Chrome, miljoenen Macs hebben een M4-chip en miljoenen browsers staan op Frans. Maar het aantal apparaten dat de nieuwste Chrome in het Frans gebruikt op een M4-chip is aanzienlijk kleiner. Voeg daar hardwaremetingen aan toe, en het wordt zo laag dat het praktisch uniek wordt.

Een fingerprint hoeft niet wereldwijd uniek te zijn; hij moet alleen uniek genoeg zijn om mensen in jouw specifieke steekproef te onderscheiden. Ter illustratie: een postcode (afgeleid van IP), een geboortedatum en geslacht zijn samen voldoende om meer dan 60% van de Amerikanen uniek te identificeren.

Informatie-entropie

Heb je weleens Akinator gespeeld? Je denkt aan een personage, een geest stelt ongeveer twintig vragen en somehow weet hij wie het is. Dat is fingerprinting in een notendop, behalve dat jouw browser de vragen voor jou beantwoordt.

Laten we dit vereenvoudigen met een spel waarbij je een personage op een bord moet raden via vragen als: "Is mijn personage een man?". Als ik "ja" zeg, en de helft van de personages zijn mannen, dan houd je slechts de helft van de kandidaten over. Dit antwoord gaf je $\log2(100 / 50) = \log2(2) = 1$ bit aan informatie, omdat het de pool met kandidaten halveerde.

De bits die je uit één antwoord haalt, worden de surprisal genoemd: hoe zeldzamer het antwoord, hoe meer het je vertelt.

  • Een kenmerk dat door 1 op de 2 mensen wordt gedeeld, is 1 bit waard (bijv. is een man).
  • 1 op de 8 is 3 bits waard (bijv. heeft een snor).
  • 1 op de 128 is 7 bits waard (bijv. heeft slechts één oog).

Surprisal kwantificeert de waarde van het antwoord, en entropie kwantificeert de waarde van de vraag. Als 1 persoon op de 128 één oog heeft, zal de vraag daarover in 127 van de 128 gevallen "nee" opleveren.

AntwoordKansSurprisalBijdrage aan entropie
Ja1/128$\log_2(128 / 1) = 7$ bits$1/128 \times 7 \approx 0,055$
Nee127/128$\log_2(128 / 127) \approx 0,0113$ bits$127/128 \times 0,0113 \approx 0,011$
Totaal$\approx 0,066$ bits

De vraag zelf is dus ongeveer 0,066 bits aan entropie waard — dat is de gemiddelde hoeveelheid bits die je per antwoord wint.

Wanneer we onafhankelijke kenmerken combineren, tellen we de bits simpelweg op. Een man met een snor en één oog zou 11 bits betekenen, of 1 op de 2048. In de praktijk is dit echter complexer (Bayesiaanse statistiek); een snor is bijvoorbeeld een sterke indicator voor een man, waardoor de kenmerken niet onafhankelijk zijn en de bits niet simpelweg optellen.

Andere toepassingen van deze redenering:

  • Medische diagnostiek: Een positief resultaat van een test die 99% accuraat is voor een ziekte die slechts 0,01% van de bevolking treft, is ongeveer 100 keer waarschijnlijker fout dan dat je daadwerkelijk de ziekte hebt. Je hebt meer bewijs nodig.
  • Cryptografie: Een wachtwoord bestaande uit vier willekeurige woorden is net zo sterk als acht willekeurige toetsenbordaanslagen (maar veel makkelijker te onthouden).
  • Forensische wetenschap: Om één persoon in een stad van een miljoen mensen aan te wijzen, zijn ongeveer 20 bits nodig. Een getuigenverklaring die overeenkomt met 1 op de 1.000 mensen levert slechts 10 bits op, waardoor er nog duizend verdachten overblijven.

Wat kun je eraan doen?

Test je browser: coveryourtracks.eff.org laat zien hoe uniek je fingerprint is, met de entropie voor elk gemeten datapunt.

  • Als je Chrome gebruikt, loop je in feite rond met een ID-kaart op je voorhoofd.
  • Safari en Firefox bieden een zeker niveau van bescherming, maar je bent nog steeds identificeerbaar door geavanceerde trackers.
  • Brave, Tor en de Mullvad Browser zijn momenteel de enige grote browsers die geavanceerde bescherming tegen fingerprinting implementeren.

Mocht je niet willen overstappen van browser: er zijn extensies zoals JShelter die fingerprinting-scripts verstoren door API-aanroepen van de browser te onderscheppen. Deze kunnen echter sommige websites breken. Hetzelfde geldt voor de Resist Fingerprinting-functie van Firefox.

De ironische waarheid is dat privacy-extensies je juist unieker maken, omdat minder mensen ze hebben. Een slimme tracker kan dit detecteren, de verstoorde signalen negeren en zich focussen op de signalen die niet verstoord zijn. Geen enkele extensie is zo effectief als bescherming op engine-niveau die in de browser is ingebouwd.

Gebruikte methoden op deze pagina

Ik creëer simpelweg een hash van een paar metingen van je apparaat. Zodra er één verandert, verandert de hash. Professionele toepassingen zijn veel complexer en kunnen identificatie behouden, zelfs als individuele variabelen veranderen.

OfflineAudioContext

Renders een korte toon via een compressor zonder deze af te spelen; kleine verschillen in het resultaat hangen af van de audio-engine van je browser en je CPU.

const off = new OfflineAudioContext(1, 5000, 44100);
// … a 1 kHz triangle wave into a dynamics compressor …
const data = (await Promise.race([off.startRendering(), timeout])).getChannelData(0);
let sum = 0;
for (let i = 4500; i < 5000; i++) sum += Math.abs(data[i]);
return sum.toFixed(3);

WebGL

Leest de fabrikant en het model van je GPU (zonder versienummers), wat de GPU en driver ondersteunen, en de pixels van een kleine gerenderde driehoek.

const gl = document.createElement('canvas').getContext('webgl');
const dbg = gl.getExtension('WEBGL_debug_renderer_info');
return gpuName(gl.getParameter(dbg ? dbg.UNMASKED_RENDERER_WEBGL : gl.RENDERER) || '');
// "ANGLE (Apple, ANGLE Metal Renderer: Apple M4, Unspecified Version)" -> "apple m4"
// …
return fnv1a(new TextEncoder().encode([...(gl.getSupportedExtensions() || []).sort(), ...limits, ...precision].join('|')));
// …
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.readPixels(0, 0, canvas.width, canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, px);

Canvas

Tekt tekst in verschillende lettertypen, emoji's en een gradiënt, en leest vervolgens de pixels terug; anti-aliasing en font-rendering hangen af van je OS, GPU en lettertypen.

ctx.font = '15px "Times New Roman", serif';
ctx.fillText('Sphinx of black quartz 🦊', 4, 22);
// …
const data = ctx.getImageData(0, 0, 240, 60).data;

Intl

Je tijdzone en je datum- en getalinstellingen; een VPN verandert je IP-locatie, maar niet je tijdzone.

const date = Intl.DateTimeFormat().resolvedOptions();
// …
return [date.timeZone, date.locale, date.calendar, date.numberingSystem, hourCycle, (1234567.891).toLocaleString()].join('/');

Navigator

Het aantal CPU-cores, je geheugengrootte (alleen in Chrome) en de taal van je browser.

return [navigator.hardwareConcurrency, navigator.deviceMemory, navigator.language].join('/');

crypto.subtle

Hasht alle waarden met SHA-256, waarna de hash een kenmerk en een dier kiest voor je bijnaam.

const digest = await crypto.subtle.digest('SHA-256', bytes);
// …
nickname: stranger || `${pick(TRAITS, hex, 0)} ${pick(ANIMALS, hex, 8)}`,