// Il Banner
Sezione 01. La presa in giroApri un sito. Compare il banner. "Questo sito utilizza cookie tecnici e di profilazione. Accetta, rifiuta, personalizza." Tre bottoni. Il primo e' grande e verde. Il secondo e' grigio, piccolo, mezzo nascosto. Il terzo ti porta in un labirinto di toggle dove devi disattivare 347 "partner" uno per uno.
GDPR. ePrivacy. Il web nel 2026 e' un campo minato di pop-up legali. Clicchi "Rifiuta tutto" e ti senti furbo. Hai detto no ai cookie. Sei al sicuro.
No. Non sei al sicuro. Il GDPR (2018) ha regolato i cookie, cioe' il meccanismo di tracciamento degli anni 2000. Roba che si salva sul disco, si legge, si cancella. Le tecniche di fingerprinting esistevano gia' (canvas fingerprinting e' del 2012, audio del 2016), ma erano di nicchia. Poi Safari nel 2020 ha bloccato i cookie third-party, Chrome ha annunciato il phase-out, e l'industria si e' spostata in massa sul fingerprinting come piano B. Il regolamento copre i cookie. Il piano B passa dalla finestra.
Esiste un altro sistema. Non salva niente sul tuo disco. Non ha bisogno del tuo consenso. Non compare in nessun banner. Si chiama browser fingerprinting e funziona cosi': il sito guarda come il tuo browser disegna un testo, che GPU hai, come suona il tuo audio engine, che font hai installato, quanti core ha la tua CPU, se usi il dark mode. Prende tutti questi pezzettini, li mette insieme, e ottiene un identificativo unico. Il tuo. Senza cookie, senza login, senza chiedere niente.
E qui arriva il bello. Molti sistemi antifrode (pagamenti online, bot detection, protezione degli account) usano il fingerprinting legalmente. Lo classificano come legitimate interest o security purpose sotto il GDPR. Il che e' sensato: se la tua banca controlla che il browser sia lo stesso di ieri per evitare frodi, ti sta proteggendo. Ma nessuno te lo spiega. Non c'e' un banner che dice "stiamo leggendo la tua GPU per verificare che sei tu". Anche quando il fingerprinting e' ammesso, resta invisibile.
La iena, quando glielo ho spiegato: "Quindi rifiuto i cookie per niente?" Piu' o meno si'. Il banner regola i cookie. Il fingerprinting passa dalla finestra.
Oggi smontiamo quella finestra pezzo per pezzo. Vediamo esattamente cosa esce, come, e perche'. E te lo mostro con i tuoi dati. Quelli che vedi nei box verdi dentro l'articolo non sono esempi inventati: sono i valori reali del tuo browser, letti in tempo reale mentre carichi la pagina. La tua GPU, i tuoi font, i tuoi core, il tuo canvas. Tutto calcolato qui, nel browser, senza mandare niente a nessuno.
// Non e' un Dato, e' un Vettore
Sezione 02. Il modello concettualePrima di entrare nel codice, una cosa va capita. Il fingerprint non e' "un dato". Non e' un numero che il sito legge da qualche parte. E' la somma di tante piccole differenze tra il tuo browser e tutti gli altri. Nessuna di queste differenze, presa da sola, ti identifica. Messe insieme, si'.
| 1 | Fingerprint = f(browser, OS, hardware, network, behaviour) |
Ogni segnale aggiunge entropia, cioe' informazione utile per distinguerti. Se un segnale ha 3 valori possibili (tipo: dark mode on, off, non supportato) aggiunge circa 1.6 bit. Se ne ha 1000, ne aggiunge circa 10. Matematicamente bastano 33 bit per distinguere 8 miliardi di persone. Ma nella pratica servono di piu', perche' i segnali non sono perfettamente indipendenti e ci sono collisioni. Gli studi dell'EFF su Panopticlick hanno mostrato che oltre l'83% dei browser ha un fingerprint unico, e con Flash o Java abilitati si arrivava al 94%. Servono piu' o meno 40-50 bit per un'identificazione stabile nel mondo reale.
L'altra cosa che conta e' la stabilita'. Un segnale che cambia ogni volta che ricarichi la pagina e' inutile. Uno che resta uguale per settimane e' oro. Il fingerprinting cerca segnali che siano unici e stabili. E ce ne sono parecchi.
Cookie vs fingerprint. Il cookie e' un post-it che il sito appiccica sul tuo browser. Lo strappi e ricomincia da zero. Il fingerprint non appiccica niente: legge quello che c'e' gia'. Non c'e' niente da staccare, niente da cancellare, niente da rifiutare in un banner. Svuoti la cache? Il fingerprint e' ancora li'. Apri la modalita' incognito? Stessa GPU, stessi font, stesso audio engine. Stesso fingerprint.
// User-Agent e Client Hints
Sezione 03. Il biglietto da visitaIl piu' vecchio e il piu' ovvio. Ogni richiesta HTTP porta con se' una stringa che dice "sono Chrome 120 su macOS". Gratis, senza JavaScript.
| 1 | navigator.userAgent |
| 2 | // "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ..." |
| 3 | |
| 4 | // Il modello nuovo: Client Hints |
| 5 | navigator.userAgentData.brands |
| 6 | // [{brand: "Chromium", version: "120"}, {brand: "Google Chrome", version: "120"}] |
Google sta congelando la stringa user-agent (UA Reduction) e spostando i dettagli veri dietro header opzionali tipo Sec-CH-UA-Platform, Sec-CH-UA-Arch, Sec-CH-UA-Model. Il server deve chiederli esplicitamente con Accept-CH. Rende il fingerprinting un pelo piu' scomodo, ma non lo blocca. E la UA ridotta stessa e' gia' un segnale: se la tua stringa e' congelata, sei su Chrome recente. Se non lo e', sei su qualcos'altro. Anche l'assenza di dati e' un dato.
// Canvas Fingerprinting
Sezione 04. Il disegno che ti tradisceQuesto e' il metodo che mi ha fatto dire "ok, e' piu' serio di quanto pensassi". Funziona cosi': disegni qualcosa su un canvas HTML5, lo converti in immagine, lo hashi. Stesso codice, stesso testo, stesso font. Ma il risultato e' diverso su ogni combinazione di browser, OS e GPU.
| 1 | const canvas = document.createElement('canvas'); |
| 2 | canvas.width = 200; canvas.height = 50; |
| 3 | const ctx = canvas.getContext('2d'); |
| 4 | |
| 5 | ctx.textBaseline = 'top'; |
| 6 | ctx.font = '14px Arial'; |
| 7 | ctx.fillStyle = '#f60'; |
| 8 | ctx.fillRect(125, 1, 62, 20); |
| 9 | ctx.fillStyle = '#069'; |
| 10 | ctx.fillText('fingerprint-test', 2, 15); |
| 11 | ctx.fillStyle = 'rgba(102,204,0,0.7)'; |
| 12 | ctx.fillText('fingerprint-test', 4, 17); |
| 13 | |
| 14 | const data = canvas.toDataURL(); // questo cambia tra browser |
Perche' cambia? Perche' "disegna un testo con Arial a 14px" sembra una cosa semplice, ma sotto ci sono almeno quattro variabili. Il font rendering: macOS usa Core Text, Windows usa DirectWrite, Linux usa FreeType. Tre engine diversi, tre risultati diversi. La GPU: l'accelerazione hardware del canvas produce pixel diversi su Intel, AMD, Apple Silicon, NVIDIA. L'anti-aliasing: subpixel RGB, BGR, greyscale, dipende dal display e dal driver. Il compositing: come vengono mescolati i colori con alpha, ogni implementazione arrotonda diversamente.
Risultato: stesso codice JavaScript, immagine diversa pixel per pixel, hash diverso. E quell'hash e' stabile nel tempo. Finche' non cambi browser, OS o GPU, resta uguale per mesi.
Questo e' il canvas che il tuo browser ha appena disegnato:
// WebGL Fingerprinting
Sezione 05. La GPU che parlaWebGL va oltre il canvas. Qui la GPU ti dice direttamente chi e'. Non devi dedurlo da un rendering. Te lo dice in chiaro.
| 1 | const gl = document.createElement('canvas').getContext('webgl'); |
| 2 | const dbg = gl.getExtension('WEBGL_debug_renderer_info'); |
| 3 | |
| 4 | // dbg puo' essere null: molti browser disabilitano questa extension |
| 5 | if (dbg) { |
| 6 | gl.getParameter(dbg.UNMASKED_VENDOR_WEBGL); |
| 7 | // "Apple" / "Google Inc. (NVIDIA)" / "Intel Inc." |
| 8 | |
| 9 | gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL); |
| 10 | // "Apple M2 Pro" / "ANGLE (NVIDIA GeForce RTX 4090 ...)" |
| 11 | } |
| 9 | |
| 10 | // Parametri che variano tra GPU |
| 11 | gl.getParameter(gl.MAX_TEXTURE_SIZE); // 16384 / 8192 / 4096 |
| 12 | gl.getParameter(gl.MAX_RENDERBUFFER_SIZE); // 16384 / 8192 |
| 13 | gl.getParameter(gl.MAX_VIEWPORT_DIMS); // [32767,32767] / [16384,16384] |
| 14 | gl.getSupportedExtensions().length; // 20 / 35 / 42 |
Vendor, renderer, texture size massima, numero di estensioni supportate. Attenzione: WEBGL_debug_renderer_info non e' sempre disponibile. Firefox l'ha rimossa nelle versioni recenti, altri browser la limitano. Ma anche quando il vendor e' mascherato, i parametri numerici (MAX_TEXTURE_SIZE, estensioni supportate, range degli alias) restano accessibili e producono comunque un'impronta. Volendo puoi anche renderizzare uno shader e hashare il risultato, perche' le GPU non implementano IEEE 754 alla lettera e lo stesso calcolo floating point produce risultati leggermente diversi su hardware diverso.
// Audio Fingerprinting
Sezione 06. Il suono che non sentiQuesto e' quello che mi ha sorpreso di piu'. Non ci pensi mai, ma il tuo browser ha un sintetizzatore audio completo. E quel sintetizzatore ha un'impronta.
| 1 | const ctx = new OfflineAudioContext(1, 44100, 44100); |
| 2 | const osc = ctx.createOscillator(); |
| 3 | const comp = ctx.createDynamicsCompressor(); |
| 4 | |
| 5 | osc.type = 'triangle'; |
| 6 | osc.frequency.setValueAtTime(10000, ctx.currentTime); |
| 7 | comp.threshold.setValueAtTime(-50, ctx.currentTime); |
| 8 | comp.knee.setValueAtTime(40, ctx.currentTime); |
| 9 | comp.ratio.setValueAtTime(12, ctx.currentTime); |
| 10 | |
| 11 | osc.connect(comp); |
| 12 | comp.connect(ctx.destination); |
| 13 | osc.start(0); |
| 14 | |
| 15 | ctx.startRendering().then(function(buffer) { |
| 16 | const data = buffer.getChannelData(0); |
| 17 | // 44100 campioni float32. L'hash e' il fingerprint. |
| 18 | }); |
OfflineAudioContext processa in memoria, non esce audio dagli speaker. Ma quei 44100 campioni float32 dipendono dall'implementazione DSP del browser e dall'arrotondamento floating point del sistema. L'hardware audio incide meno di quanto si pensi: la differenza vera la fa l'implementazione WebAudio del browser (Blink in Chrome, cubeb in Firefox, WebKit in Safari). Le API di sistema come Core Audio o WASAPI servono solo per l'output, non per il rendering dell'OfflineAudioContext. Stesso codice, numeri leggermente diversi, hash diverso. Non ci senti niente, ma il sito ti ha sentito benissimo.
// Font Fingerprinting
Sezione 07. I font che hai installatoNon puoi chiedere al browser "dammi la lista dei font installati". Ma puoi dedurla. Il trucco: crei un testo con un font di base (monospace), misuri le dimensioni. Poi cambi il font con uno specifico. Se le dimensioni cambiano, quel font e' installato.
| 1 | function hasFont(font) { |
| 2 | const span = document.createElement('span'); |
| 3 | span.textContent = 'mmmmmmmmmmlli'; |
| 4 | span.style.fontSize = '72px'; |
| 5 | document.body.appendChild(span); |
| 6 | |
| 7 | span.style.fontFamily = 'monospace'; |
| 8 | const baseWidth = span.offsetWidth; |
| 9 | |
| 10 | span.style.fontFamily = '"' + font + '", monospace'; |
| 11 | const changed = span.offsetWidth !== baseWidth; |
| 12 | span.remove(); |
| 13 | return changed; |
| 14 | } |
La lista dei font installati dice piu' di quanto pensi. macOS ha Helvetica Neue, Windows ha Segoe UI. Hai Calibri? Hai installato Office. Hai Myriad Pro? Hai installato la suite Adobe. Hai font CJK? Probabilmente parli cinese, giapponese o coreano. Testi 50 font e hai un'impronta del sistema operativo, del software installato, e della lingua. Va detto: i browser moderni stanno limitando questa tecnica. Chrome e Firefox applicano font visibility restrictions che riducono il set di font rilevabili a quelli di sistema. Il fingerprint via font e' meno preciso di qualche anno fa, ma non e' morto.
// Screen Fingerprinting Avanzato
Sezione 08. Oltre width e height
screen.width e screen.height sono l'antipasto. La roba interessante sta sotto.
| 1 | // Scaling: un Mac Retina ha 2, un Android puo' avere 2.625 o 3.5 |
| 2 | window.devicePixelRatio // quel decimale e' sorprendentemente raro |
| 3 | |
| 4 | // 24 = standard, 30 = HDR, 16 = probabilmente una VM |
| 5 | screen.colorDepth |
| 6 | |
| 7 | // Gamut: display P3 = Apple o monitor pro. rec2020 = HDR recente |
| 8 | matchMedia('(color-gamut: p3)').matches |
| 9 | matchMedia('(color-gamut: rec2020)').matches |
| 10 | |
| 11 | // Preferenze OS: persistenti, non cambiano con incognito |
| 12 | matchMedia('(prefers-color-scheme: dark)').matches |
| 13 | matchMedia('(prefers-reduced-motion: reduce)').matches |
| 14 | matchMedia('(prefers-contrast: more)').matches |
Il devicePixelRatio con quei decimali assurdi tipo 2.625 restringe parecchio il campo. Un colorDepth di 16 e' quasi certamente una VM. E le preferenze (dark mode, reduced motion, high contrast) sono impostazioni del sistema operativo. Restano uguali tra sessioni, tra incognito, tra profili browser. Chi usa dark mode + reduced motion + high contrast e' una combinazione abbastanza rara. E la rarita' e' entropia.
// Hardware Info
Sezione 09. Quello che il browser regalaQueste le chiedi e te le da'. Senza trucchi, senza rendering, senza hack.
| 1 | navigator.hardwareConcurrency // 8 / 10 / 16 / 24 core |
| 2 | navigator.deviceMemory // 4 / 8 GB (arrotondato) |
| 3 | navigator.maxTouchPoints // 0 = desktop, 5 = phone, 10 = tablet |
| 4 | |
| 5 | Intl.DateTimeFormat().resolvedOptions().timeZone // "Europe/Rome" |
| 6 | navigator.languages // ["it-IT", "en-US", "en"] |
"12 core, 8 GB, 0 touch points, Europe/Rome, it-IT e en-US." Gia' cosi' il cerchio si restringe parecchio. Mettici sopra canvas, WebGL, audio, e la ricerca diventa piu' mirata di un cecchino con il mirino termico.
// Timing Attacks
Sezione 10. Il tempo come segnale| 1 | const t0 = performance.now(); |
| 2 | for (let i = 0; i < 100000; i++) Math.atan(2); |
| 3 | const elapsed = performance.now() - t0; |
Quanto ci mette a fare 100.000 operazioni matematiche? Dipende dalla CPU, dalla microarchitettura, dalla cache, dal JIT compiler. Non basta da solo per identificarti, ma aggiunge qualche bit al vettore complessivo. Dopo Spectre i browser hanno ridotto la precisione di performance.now(), ma anche la quantita' di jitter aggiunto e' un segnale: ogni browser lo fa in modo diverso.
// Speech Synthesis
Sezione 11. Le voci nel browserQuesta e' la mia preferita perche' nessuno ci pensa. Il browser ha un sintetizzatore vocale. E la lista delle voci installate e' diversa su ogni sistema.
| 1 | speechSynthesis.onvoiceschanged = function() { |
| 2 | const voices = speechSynthesis.getVoices(); |
| 3 | // macOS: "Samantha", "Alex", "Alice" |
| 4 | // Windows: "Zira", "David", "Mark" |
| 5 | // Linux: "eSpeak" o niente |
| 6 | const fp = voices.map(v => v.name + '|' + v.lang).join(','); |
| 7 | }; |
La lista dipende da OS, versione, lingua configurata, pacchetti TTS installati. E' stabile tra sessioni. Cambia solo quando aggiorni il sistema o installi nuove voci. Un modo elegante per sapere che OS usi senza chiedertelo.
// Permissions API
Sezione 12. La firma comportamentale| 1 | const perms = ['camera', 'microphone', 'geolocation', 'notifications']; |
| 2 | |
| 3 | Promise.all(perms.map(function(name) { |
| 4 | return navigator.permissions.query({ name: name }) |
| 5 | .then(function(r) { return name + ':' + r.state; }); |
| 6 | })); |
| 7 | // ["camera:denied", "microphone:granted", "geolocation:prompt", ...] |
granted, denied, prompt. Questo stato e' persistente e dipende da quello che hai fatto in passato. Non e' hardware, non e' software: e' una foto delle tue scelte. Camera negata, microfono concesso, notifiche bloccate. Una combinazione diversa per quasi tutti.
// WebRTC IP Leak
Sezione 13. L'IP che esce dalla VPN| 1 | const pc = new RTCPeerConnection({ iceServers: [] }); |
| 2 | pc.createDataChannel(''); |
| 3 | pc.createOffer().then(o => pc.setLocalDescription(o)); |
| 4 | |
| 5 | pc.onicecandidate = function(e) { |
| 6 | if (e.candidate) console.log(e.candidate.candidate); |
| 7 | // "candidate:842163049 1 udp 192.168.1.100 ..." |
| 8 | }; |
Il classico. Crei una connessione WebRTC, il browser raccoglie i candidati ICE, e fra quelli c'era il tuo IP locale. In passato usciva anche dietro VPN. Oggi la situazione e' diversa: Chrome dal 2021 usa mDNS per mascherare l'IP locale nei candidati ICE, Firefox e Safari hanno mitigazioni simili. Nella maggior parte dei casi l'IP reale non esce piu'. Resta utile come segnale (il comportamento ICE stesso varia tra browser), ma il leak diretto dell'IP locale e' in gran parte risolto.
// Bounce Tracking
Sezione 14. L'evoluzione del cookieI browser hanno introdotto protezioni (ITP in Safari, ETP in Firefox, Storage Partitioning in Chrome) che separano cookie e storage per sito. Ma esiste un trucco che le aggira.
Il redirect dura millisecondi. Non lo vedi nemmeno. Ma in quei millisecondi il tracker imposta un cookie first-party (perche' tu stai visitando il suo dominio, tecnicamente). Non e' fingerprinting puro, ma fa parte dello stesso ecosistema: tracciarti senza che tu te ne accorga.
// Network Fingerprint
Sezione 15. JA3 e lo stack TCPIl fingerprinting non si ferma al browser. Quando fai una connessione HTTPS, il primo messaggio (ClientHello) contiene un set specifico di cipher suites, estensioni, curve ellittiche. Quel set e' diverso tra browser e versioni.
| 1 | JA3 = MD5(TLSVersion, Ciphers, Extensions, Curves, PointFormats) |
Si chiama JA3 fingerprint (la versione moderna e' JA4, piu' granulare). Ogni combinazione di browser, libreria TLS, sistema operativo e eventuale proxy produce un hash diverso. Chrome su macOS e Chrome su Linux hanno JA3 diversi. Chrome dietro Cloudflare ha un JA3 diverso da Chrome diretto. Non si puo' cambiare lato client senza modificare lo stack TLS. E sotto c'e' anche lo stack TCP (window size, TTL, opzioni): dipende dal kernel, quindi dall'OS. Il server non deve fare niente di speciale. Basta ascoltare il primo pacchetto.
// Fingerprint a Livello OS
Sezione 16. Clock skew, battery, sensoriQui si va piu' a fondo. Il clock skew e' la deviazione dell'orologio interno del tuo computer rispetto al tempo reale. Ogni cristallo di quarzo devia un po' diversamente. Misurando il drift su piu' richieste si ottiene una firma che e' unica per macchina. Non cambia con il browser, la VPN, o la rete. Cambi IP? Il clock skew resta lo stesso. Tracking cross-network.
La Battery API (navigator.getBattery()) espone livello di carica, tempo di ricarica, stato di caricamento. "87%, in carica, 45 minuti alla fine" e' piuttosto unico in un dato momento. I browser hanno iniziato a rimuoverla per questo motivo.
Le Sensor API (accelerometro, giroscopio) sui telefoni espongono dati che dipendono dalla calibrazione hardware. Due telefoni identici possono avere offset diversi nei sensori, il che li rende distinguibili.
// WASM Timing
Sezione 17. Il cronometro precisoIl WebAssembly produce tempi di esecuzione piu' stabili del JavaScript perche' bypassa il JIT (che aggiunge rumore). Questo lo rende un cronometro piu' preciso. E un cronometro preciso e' un problema per la privacy.
| 1 | const t0 = performance.now(); |
| 2 | WebAssembly.instantiate(wasmModule).then(function() { |
| 3 | const compilationTime = performance.now() - t0; |
| 4 | // dipende da: CPU, cache, pipeline |
| 5 | // puo' distinguere Intel da AMD, ARM da x86 |
| 6 | }); |
Tempo di compilazione, throughput di loop numerici, latenza di accesso alla memoria. Tutto dipende dalla microarchitettura della CPU. Si possono distinguere famiglie di processori anche quando il browser nasconde hardwareConcurrency. Il WASM e' una lente piu' pulita del JS per guardare l'hardware.
// WebGPU e Compute Shader
Sezione 18. La nuova frontieraWebGPU e' il successore di WebGL e espone molto piu' della GPU. E' la prossima grande superficie di fingerprinting. Quasi nessun articolo ne parla perche' l'API e' ancora giovane, ma i dati che espone sono una miniera.
| 1 | const adapter = await navigator.gpu.requestAdapter(); |
| 2 | |
| 3 | // L'API non e' ancora stabile ovunque. |
| 4 | // In alcuni browser serve adapter.requestAdapterInfo() |
| 5 | adapter.info.vendor // "apple" / "nvidia" / "amd" |
| 6 | adapter.info.architecture // "common-3" (Apple M2) |
| 5 | |
| 6 | // Limiti hardware specifici della GPU |
| 7 | adapter.limits.maxTextureDimension2D // 16384 |
| 8 | adapter.limits.maxComputeWorkgroupsPerDimension // 65535 |
| 9 | adapter.limits.maxBufferSize // 268435456 |
| 10 | |
| 11 | adapter.features // Set {"texture-compression-bc", "float32-filterable", ...} |
E la parte piu' potente: puoi scrivere un compute shader in WGSL, eseguirlo sulla GPU, leggere il buffer di output e hasharlo. Le differenze derivano dal driver, dall'architettura del processore grafico, e dal comportamento floating point. E' il canvas fingerprinting della prossima generazione, ma con il controllo completo della pipeline di calcolo.
Se vedi "non supportato" e' normale: WebGPU funziona solo su Chrome e Edge. Firefox e Safari non lo implementano ancora. Anche questo e' un segnale di fingerprinting: l'assenza dell'API dice qualcosa sul tuo browser.
// Mappa dell'Entropia
Sezione 19. Quanto vale ogni segnaleStime basate su studi accademici e dati di Panopticlick/AmIUnique. La somma aritmetica supera i 50 bit, ma i segnali non sono indipendenti (chi ha macOS ha anche Core Text, quindi canvas e font sono correlati). L'entropia effettiva e' piu' bassa, tipicamente 33-45 bit. Basta comunque. Ogni nuova API del browser aggiunge superficie. L'ironia: le API esistono perche' servono a fare cose utili (rendering, audio, accessibilita'). Ma ognuna di loro lascia una traccia.
// Chi Si Difende e Come
Sezione 20. Tre filosofieI browser provano a difenderti. Ognuno a modo suo.
Tor Browser: tutti devono apparire identici. Stessa risoluzione, stessi font, stesse API disabilitate. Se il fingerprint e' uguale per tutti, non identifica nessuno. Funziona, ma navigare e' un calvario.
Brave: aggiunge rumore casuale ai risultati di canvas, WebGL e audio. Ogni sessione ha un fingerprint diverso. Il tracking cross-session si rompe perche' l'hash cambia ogni volta.
Safari: riduce la quantita' di informazione disponibile. Limita le API, congela i font, usa ITP per partizionare lo storage. Non uniforma, non randomizza: toglie dati.
| Browser | Strategia | Canvas | WebGL | Audio | Fonts |
|---|---|---|---|---|---|
| Tor | Uniformare | Identico | Disabilitato | Disabilitato | Set fisso |
| Brave | Randomizzare | Random | Random | Random | Esposti |
| Safari | Ridurre | Esposto | Esposto | Esposto | Limitati |
| Firefox | Misto | Opt-in | Esposto | Esposto | Opt-in |
| Chrome | Indiretta (UA reduction, Privacy Sandbox, IP Protection in rollout) | Esposto | Esposto | Esposto | Esposti |
// Come Funziona Nella Realta'
Sezione 21. ML, correlazione, e il paradossoNessuno usa una sola tecnica. I sistemi reali (antifrode, bot detection, ad tech) prendono tutti i segnali, li pesano con machine learning, e producono un punteggio: "con probabilita' del 97.3%, questa sessione e' lo stesso utente della sessione X." Non serve un match esatto. Basta una correlazione alta.
E poi c'e' il behavioural fingerprint: come muovi il mouse (velocita', accelerazione, curvatura), come scrolli (inerzia, frequenza), come digiti sulla tastiera (cadenza tra un tasto e l'altro, tempo di pressione). I sistemi antifrode delle banche lo usano pesantemente. Se digiti la password con un timing diverso dal solito, scatta un alert. Anche quelli sono identificanti. Ma questo e' un altro articolo.
Il paradosso. Piu' ti proteggi, piu' diventi raro. Usi Tor con WebGL disabilitato e un set di font fisso? Sei uno dei pochi a farlo. I gruppi piccoli sono facili da tracciare. L'unica vera difesa e' essere indistinguibile dalla massa. E la massa usa Chrome senza estensioni.
La modalita' incognito? Non cambia il fingerprint. Non cambia la GPU, non cambia i font, non cambia la risoluzione, non cambia l'audio. Cambia solo una cosa: non manda i cookie. Per il fingerprinting stateless, incognito e' trasparente. Come mettersi gli occhiali da sole e pensare di essere irriconoscibile.
// Contromisure
Sezione 22. Cosa puoi fare (e cosa no)| Contromisura | Cosa fa | Efficacia | Costo |
|---|---|---|---|
| Tor Browser | Fingerprint uniforme | Alta | Lento, siti rotti |
| Brave (shields) | Randomizzazione | Media | Rari glitch |
| VM | Hardware virtuale | Alta | Pesante, scomodo |
| Disabilitare WebGL | Toglie un segnale | Bassa | Google Maps rotto, ti rende raro |
| Incognito | Niente cookie | Zero | Nessuno (perche' non fa niente) |
Il problema di fondo: meno entropia = meno usabilita'. Disabili WebGL e Google Maps non funziona. Blocchi i font e i siti sono brutti. Usi Tor e ci metti 10 secondi a caricare una pagina. Ogni contromisura ha un costo. E la maggior parte delle persone non e' disposta a pagarlo. I tracker lo sanno bene.
// Il Tuo Fingerprint
Sezione 23. Il risultato finaleSe hai letto fin qui, hai gia' visto tutto. Ogni box verde che hai incontrato nell'articolo conteneva dati reali del tuo browser, letti in tempo reale. Il tuo canvas, la tua GPU, il tuo audio engine, i tuoi font, i tuoi core, il tuo timezone. Nessun esempio inventato. Tutto tuo.
Qui sotto c'e' l'hash SHA-256 calcolato combinando tutti quei segnali. Questo e' il tuo fingerprint. Prova ad aprire la stessa pagina in incognito: l'hash sara' quasi identico. Prova con un altro browser: sara' diverso. Questa e' la dimostrazione pratica di tutto quello che hai letto.
// Conclusione
ChiudiamoHai presente quel banner? "Accetta i cookie." L'hai rifiutato. Ti senti al sicuro. Ma il sito ha gia' guardato come il tuo browser disegna un testo, che GPU hai, quanti core ha la tua CPU, che voci di sintesi sono installate, se usi il dark mode, che font hai. Ha messo tutto insieme e ha un identificativo unico. Il tuo. Senza aver scritto un solo byte sul tuo disco.
Il fingerprinting non e' illegale. Serve per antifrode, bot detection, sicurezza bancaria. Ma e' anche usato per tracking pubblicitario senza consenso. La stessa tecnica, due usi opposti. La differenza non e' nel codice. E' nell'intenzione. E il banner dei cookie non copre nessuno dei due casi.
La iena, riassumendo dopo aver letto il tutto: "Quindi quei pop-up non servono a niente?" Non a niente. Servono ai cookie. Ma per il fingerprinting e' come mettere una grata sulla porta e lasciare tutte le finestre aperte.
Non devi dire al browser chi sei. Lo sa gia'.
21 superfici. 33-45 bit. Zero cookie. Zero consensi.
Il banner e' una grata. Le finestre sono aperte.