Twoja przeglądarka zdradza system ostatnimi bitami wyniku. Jak działa fingerprinting bez cookies
Wpisujesz w przeglądarce Math.tanh(0.8). Dostajesz liczbę. Zwykły wynik funkcji matematycznej, którego nikt przy zdrowych zmysłach nie uznałby za informację o swoim komputerze.
A jednak jej ostatnie cyfry mogą zdradzić system operacyjny, na którym działa Chromium.
Nie imię, nie numer seryjny procesora i nie dokładny model laptopa. Coś subtelniejszego, a w praktyce bardzo użytecznego: odpowiedź na pytanie, czy historia opowiadana przez przeglądarkę trzyma się kupy. Jeśli User-Agent mówi „macOS”, ale matematyka, fonty, WebGL i warstwa sieciowa zachowują się jak Linux uruchomiony w kontenerze, powstaje całkiem dobry powód, żeby przyjrzeć się tej sesji dokładniej.
Na co dzień używam automatycznych przeglądarek do testów, audytów i zbierania danych. Playwright jest jednym z moich podstawowych narzędzi. Im głębiej wchodzę w to, jak serwisy próbują odróżnić prawdziwego użytkownika od automatu, tym mniej sensowna wydaje mi się popularna opowieść: „wyczyściłem cookies, mam adblocka, więc nikt nie wie, że to ja”.
Punktem wyjścia były dla mnie dwie świetne dyskusje na Hacker News: jedna o wykrywaniu systemu przez różnice w funkcjach matematycznych, druga o tym, ile naprawdę mówi fingerprint przeglądarki. Same artykuły są ciekawe, ale dopiero komentarze odsłaniają właściwy spór: unikalny wynik nie musi być trwały, przypadkowe fałszowanie cech może zaszkodzić, a ochrona przed botami i śledzenie ludzi korzystają czasem z tych samych danych.
Adblock ma sens. Tryb prywatny ma sens. VPN ma sens. Żaden z nich nie jest jednak przyciskiem „stań się niewidzialny”.
Test wykonywany lokalnie
Co wiemy o Tobie — a dokładniej co mówi wynik Twojej przeglądarki
Math.tanh(0.8)
uruchom JavaScript, aby wykonać test
Poniżej nadal znajdziesz wyniki referencyjne. Automatyczne porównanie wymaga JavaScriptu.
- Przeglądarka
- do odczytania lokalnie
- Deklarowana platforma
- do odczytania lokalnie
- Co naprawdę wiemy
- sam wynik nie identyfikuje urządzenia
Test działa wyłącznie w tej karcie. Wyniku nie zapisujemy ani nie wysyłamy.
Ostatnie bity wyniku
Punktem wyjścia do tego tekstu był opis zmiany w silniku V8. Chromium przestało korzystać z własnej implementacji części funkcji matematycznych i zaczęło wywoływać funkcje dostarczane przez bibliotekę systemową. Można to zobaczyć w konkretnym commicie V8.
Różne systemy mogą liczyć ten sam wynik odrobinę inaczej. Nie na poziomie istotnym dla człowieka. Na poziomie ostatnich bitów liczby zmiennoprzecinkowej — jak najbardziej.
Uruchomiłem punktowy test na macOS w systemowym Chrome 150 sterowanym przez Playwrighta. Math.tanh(0.8) zwróciło 0.664036770267849, czyli wartość przypisaną macOS w analizie Scrapfly. To nie dowodzi, że jedna funkcja niezawodnie rozpoznaje każdy system i każdą wersję Chrome. Potwierdza coś ciekawszego: kod strony może zaglądać w miejsca, których użytkownik w ogóle nie kojarzy z informacją o swoim urządzeniu.
Źródłowy test na trzech rzeczywistych maszynach pokazuje też, dlaczego detektor musi dobrać odpowiednie wejście. Dla 0.5 wszystkie systemy zwracają identyczny wynik. Przy 0.7 odstaje Linux, przy 0.9 Windows, a 0.8 rozdziela wszystkie trzy:
| Wywołanie | Linux — glibc | macOS — libsystem_m | Windows — UCRT | Twoja przeglądarka |
|---|---|---|---|---|
Math.tanh(0.5)pełna zgodność referencji |
0.46211715726000974 |
0.46211715726000974 |
0.46211715726000974 |
wymaga JavaScriptu |
Math.tanh(0.7)Linux odstaje o 1 ULP |
0.6043677771171636 |
0.6043677771171635 |
0.6043677771171635 |
wymaga JavaScriptu |
Math.tanh(0.8)trzy różne wyniki |
0.6640367702678491 |
0.664036770267849 |
0.6640367702678489 |
wymaga JavaScriptu |
Math.tanh(0.9)Windows odstaje o 1 ULP |
0.7162978701990245 |
0.7162978701990245 |
0.7162978701990244 |
wymaga JavaScriptu |
Żółte podświetlenie pokazuje końcówkę, w której zaczynają się różnice. Kolumna „Twoja przeglądarka” jest obliczana lokalnie i nie jest nigdzie wysyłana.
Żółte podświetlenie zaczyna się w miejscu, w którym kończy się wspólny zapis dziesiętny. ULP — unit in the last place — oznacza najmniejszą różnicę, jaką w tym miejscu może zapisać liczba typu double. JavaScript pokazuje najkrótszy zapis dziesiętny pozwalający odtworzyć tę samą wartość binarną, dlatego wynik macOS dla 0.8 ma o jedną widoczną cyfrę mniej. Pomiary wykonał Scrapfly przez DevTools Protocol na Chrome 150: Linuxie z glibc, macOS 26 na Apple Silicon oraz Windows 11 z UCRT.
Sam wynik tanh nie jest unikalnym identyfikatorem. Jest testem spójności. Detektor może zestawić go z danymi z kilkunastu innych warstw i zapytać: czy wszystkie te cechy mogły powstać w jednym prawdziwym środowisku?
Bot, fałszywa przeglądarka i powracający użytkownik to trzy różne problemy
W dyskusjach o fingerprintingu regularnie miesza się trzy pytania:
- Czy ta sesja jest sterowana automatycznie?
- Czy deklarowana przeglądarka, system i urządzenie tworzą wiarygodną całość?
- Czy to ten sam klient, którego widzieliśmy wczoraj?
To nie są synonimy.
Zwykły użytkownik może mieć bardzo charakterystyczny fingerprint, ale nie być botem. Automat może używać prawdziwego profilu Chrome i wyglądać całkiem zwyczajnie. Nietypowa konfiguracja może należeć do osoby korzystającej z technologii wspomagającej, korporacyjnego proxy albo restrykcyjnych ustawień prywatności.
Dlatego sensowne systemy nie powinny wydawać wyroku na podstawie jednego pola. Składają ocenę z wielu sygnałów. Cloudflare opisuje wprost połączenie heurystyk, testów JavaScript, modeli uczących się i danych o sesji w swojej dokumentacji mechanizmów bot detection.
Najlepsze detektory nie pytają więc: „czy navigator.webdriver ma wartość true?”. Pytają: „czy setki sygnałów opowiadają tę samą historię?”.
Co wysyłasz, zanim strona uruchomi JavaScript
Fingerprinting kojarzy się zwykle ze skryptem rysującym coś na niewidocznym canvasie. Tymczasem serwer dostaje pierwszą porcję informacji, zanim przeglądarka wykona choćby jedną linijkę JavaScriptu.
Widzi między innymi:
- adres IP, operatora i przybliżoną lokalizację;
- nagłówki HTTP, ich wartości i kolejność;
- User-Agent, Client Hints, język i preferowane formaty;
- sposób zestawienia TLS;
- parametry HTTP/2 albo HTTP/3;
- charakterystykę połączenia i historię reputacji adresu.
Bez cookies i bez kliknięcia „zgadzam się”
Tyle strona wie o tej sesji już teraz
| Sygnał | Co widać | Skąd |
|---|---|---|
| Adres IP i połączenie | Serwer musiał je zobaczyć, aby odesłać tę stronę. Ten widget celowo nie pobiera ani nie wyświetla Twojego IP. | żądanie HTTP |
| User-Agent | wymaga JavaScriptu |
HTTP + JavaScript |
| Platforma | wymaga JavaScriptu |
Client Hints + JavaScript |
| Języki | wymaga JavaScriptu |
HTTP + JavaScript |
| Strefa czasowa | wymaga JavaScriptu |
JavaScript |
| Ekran | wymaga JavaScriptu |
JavaScript |
| Okno strony | wymaga JavaScriptu |
JavaScript |
| CPU i pamięć | wymaga JavaScriptu |
JavaScript |
| Obsługa dotyku | wymaga JavaScriptu |
JavaScript |
| Sygnały prywatności | wymaga JavaScriptu |
JavaScript |
| Skąd tu przyszedłeś | wymaga JavaScriptu |
HTTP + JavaScript |
Adblock nadal pomaga, bo może zatrzymać zewnętrzny tracker. Nie odbiera jednak kodowi first-party uruchomionemu razem ze stroną dostępu do tych API. Żaden z powyższych odczytów nie wymaga zapisania cookie.
Widget nie wykonuje dodatkowych żądań, nie zapisuje wyniku i nie wysyła dynamicznych odczytów do Critical.pl.
Samo Accept-Language potrafi być zaskakująco szczegółowe. W jednej z dyskusji na Hacker News użytkownik zauważył, że wybieranie w Chrome opcji „nigdy nie tłumacz tego języka” zbudowało mu nietypową, uporządkowaną listę języków wysyłaną do odwiedzanych stron. Google przygotowało nawet propozycję ograniczenia informacji w tym nagłówku, właśnie ze względu na pasywny fingerprinting.
Jeszcze ciekawiej robi się na poziomie TLS i HTTP/2. Przeglądarka, Python, biblioteka Go i reverse proxy mogą inaczej układać rozszerzenia TLS, cipher suites, ustawienia połączenia i pseudo-nagłówki. Dlatego automat może deklarować w JavaScripcie, że jest najnowszym Chrome na Windowsie, a warstwa sieciowa nadal wyglądać jak klient HTTP uruchomiony z serwera.
Zmiana samego User-Agenta jest jak założenie cudzej koszulki bez zmiany głosu, chodu i dokumentów.
Co strona może policzyć w twojej karcie
Po uruchomieniu JavaScriptu powierzchnia robi się znacznie większa.
Przeglądarka, ekran i sprzęt
Strona może poznać język, strefę czasową, rozmiar ekranu i viewportu, skalowanie, głębię kolorów, liczbę logicznych rdzeni procesora, przybliżoną pamięć urządzenia, obsługiwane API, touch, preferencje kontrastu czy ograniczenie animacji.
Żadna z tych informacji nie musi być szczególnie rzadka. Problemem jest ich kombinacja.
Canvas, WebGL i audio
Canvas pozwala narysować ustalony tekst oraz kształty, odczytać piksele i porównać wynik. Różnice biorą się z fontów, rasteryzacji, systemu, GPU i sterownika. WebGL dodaje informacje o rendererze, limitach i sposobie wykonywania shaderów. Web Audio może wygenerować dźwięk w pamięci — bez odtwarzania go w głośnikach — a następnie porównać próbki.
To nie znaczy, że jeden hash canvasu zna numer seryjny karty graficznej. Zazwyczaj rozpoznaje klasę konfiguracji. W połączeniu z pozostałymi cechami może jednak istotnie zawęzić zbiór podobnych klientów.
Fonty i zachowanie implementacji
Lista dostępnych fontów zdradza więcej, niż mogłoby się wydawać: system, język, czasem zawód albo zainstalowane oprogramowanie. Do tego dochodzą różnice w CSS, obsłudze błędów, precyzji layoutu, kodekach, WebAssembly i funkcjach matematycznych takich jak tanh.
Detektor nie potrzebuje magicznej właściwości o ogromnej unikalności. Może budować profil z wielu nudnych, słabych sygnałów.
Po czym widać Playwrighta, Puppeteera i Selenium
Najprostszy test jest częścią standardu. W3C definiuje navigator.webdriver jako informację, że przeglądarka jest sterowana przez WebDriver.
To użyteczne wobec zwykłej, nieukrywanej automatyzacji. Jest też banalne do nadpisania, więc brak tej flagi niczego nie dowodzi.
Kolejne warstwy to między innymi:
- pozostałości po ChromeDriverze i skryptach inicjalizujących;
- nietypowe deskryptory właściwości oraz prototypy;
- różnice widoczne w iframe’ach i workerach, których nie objęła łatka;
- ślady komunikacji przez Chrome DevTools Protocol;
- renderer programowy, brak typowych fontów albo urządzeń multimedialnych;
- rozdzielczości i konfiguracje charakterystyczne dla maszyn wirtualnych;
- timing, regularność event loop i zachowanie pod obciążeniem;
- syntetyczne ruchy myszy, scroll, rytm wpisywania i kolejność zdarzeń;
- nieludzkie tempo oraz powtarzalność całej sesji.
Playwright i Puppeteer sterują Chromium przez Chrome DevTools Protocol. CDP jest świetnym interfejsem do debugowania i automatyzacji, ale nie powstało po to, żeby być niewidoczne. Niektóre testy wykorzystują efekty uboczne serializacji obiektów, stack traces i subskrybowania zdarzeń runtime.
To nadal nie jest dowód. Człowiek może mieć otwarte DevTools. Narzędzie dostępnościowe może sterować stroną. Monitoring syntetyczny z definicji zachowuje się jak automat. CDP powinno być cechą modelu, nie wyrokiem.
Stealth nie usuwa problemu. Przesuwa go o warstwę niżej
Historia narzędzi „stealth” wygląda jak klasyczny wyścig zbrojeń:
- Detektor sprawdza
navigator.webdriver. - Automat ukrywa flagę.
- Detektor sprawdza deskryptor, prototyp albo nowy iframe.
- Automat poprawia przeglądarkę na niższym poziomie.
- Detektor porównuje TLS, HTTP/2, grafikę, timing i zachowanie.
- Automat korzysta z prawdziwego profilu, residential proxy i nagranych interakcji.
- Obrona koreluje wiele kont, historię sesji i wzorce nadużyć.
Nie istnieje jedna „undetectable browser”. Istnieje koszt utrzymania wiarygodnej persony oraz koszt pomyłki po obu stronach.
Co gorsza, prymitywne fałszowanie może zwiększyć wykrywalność. Losowy szum nie zawsze pomaga. Jeśli canvas zmienia się przy każdym odczycie albo przeglądarka podaje kombinację cech, która nie występuje na żadnym realnym urządzeniu, samo fałszerstwo staje się sygnałem.
Najgorsze jest to, że fingerprint nie musi być jednym hashem
Łatwo uspokoić się myślą: „mój fingerprint zmieni się po aktualizacji, więc nie da się mnie śledzić”.
Rzeczywiście, fingerprint się zmienia. Aktualizujesz system i przeglądarkę, instalujesz font, podłączasz monitor, podróżujesz do innej strefy czasowej. To ważne ograniczenie.
Tracker nie potrzebuje jednak wiecznego numeru seryjnego. Może utrzymywać graf hipotez:
- profil A i A’ różnią się wyłącznie wersją Chrome;
- oba pojawiają się z podobnego operatora i w tej samej strefie czasowej;
- jeden zniknął dokładnie wtedy, gdy pojawił się drugi;
- oba poruszają się po serwisie w podobny sposób;
- oba w końcu logują się na to samo konto.
Badanie FP-STALKER pokazało, że zmieniające się fingerprinty można wiązać w czasie. To nadal model probabilistyczny, nie nieomylne rozpoznawanie człowieka. W wielu zastosowaniach „bardzo prawdopodobne” w zupełności jednak wystarcza.
Adblock, incognito i VPN: pomagają, ale nie czynią cię niewidzialnym
Adblock
Adblock realnie blokuje znane skrypty, endpointy i synchronizację identyfikatorów. Kod, który nie został pobrany, nie wykona fingerprintingu i nie wyśle jego wyniku. To duża korzyść.
Nie zablokuje jednak automatycznie kodu first-party, self-hosted albo nowego. Sama konfiguracja blokowania może też wpływać na DOM, timing i listę wykonanych żądań, stając się jednym z sygnałów. Wniosek nie brzmi „adblock nic nie daje”. Brzmi: adblock ogranicza ekspozycję, ale nie usuwa fingerprintu przeglądarki.
Tryb prywatny
Tryb prywatny przede wszystkim izoluje i usuwa stan: historię, cookies oraz część magazynów po zamknięciu sesji. Nie gwarantuje anonimowości wobec odwiedzanej strony. IP, TLS, konfiguracja przeglądarki i wiele cech urządzenia nadal są widoczne.
VPN
VPN ukrywa domowy adres IP przed stroną i może powiększyć grupę osób wychodzących przez jeden serwer. Nie zmienia automatycznie fontów, WebGL, języka, zachowania ani konta, na które się logujesz. Adres znanego VPN-u może też podnieść ocenę ryzyka w systemie antyfraudowym.
Każde z tych narzędzi rozwiązuje realny fragment problemu. Rozczarowanie zaczyna się dopiero wtedy, gdy oczekujemy od jednego z nich pełnej niewidzialności.
Ta sama technika może chronić konto albo śledzić reklamowo
Fingerprinting nie ma jednego zastosowania.
Bank może używać podobnych cech do wykrycia przejęcia konta. Sklep może walczyć z credential stuffingiem, masowym wykupywaniem limitowanego produktu albo fraudem płatniczym. Serwis może ograniczać scraping i automatyczne zakładanie kont.
Ta sama telemetria może jednak służyć do łączenia wizyt, obchodzenia blokowania cookies oraz budowania profilu reklamowego.
Technika nie rozstrzyga celu. Znaczenie mają zakres, podstawa, retencja, przejrzystość oraz to, czy użytkownik ma realny wybór. Problem pojawia się również po stronie obrony: restrykcyjne ustawienia prywatności, VPN, nietypowa przeglądarka albo technologia wspomagająca mogą wyglądać „podejrzanie”. Jeśli system blokuje na podstawie pojedynczej cechy, będzie karał również prawdziwych ludzi.
Sprawdź, co potrafi zobaczyć strona
Publiczne testy pozwalają obejrzeć tę powierzchnię z różnych stron:
- EFF Cover Your Tracks pokazuje fingerprint i działanie ochron przed trackerami na tle populacji użytkowników testu;
- AmIUnique zbiera szeroki zestaw atrybutów i bada zmianę fingerprintu w czasie;
- BrowserLeaks rozdziela testy canvas, WebGL, audio, fontów, WebRTC, TLS i HTTP/2;
- oryginalne demo CreepJS agresywnie sprawdza spójność wielu cech środowiska.
Wynik „unikalny” nie oznacza „jesteś jedyną taką osobą w Internecie”. Oznacza najwyżej, że w konkretnej, samodzielnie dobranej bazie testu nie znaleziono identycznego profilu. Każde z tych narzędzi ma też własną politykę zbierania danych — warto przeczytać ją przed uruchomieniem.
Dlatego dodałem do tego artykułu prosty test działający wyłącznie w przeglądarce. Bez wysyłania wyniku na serwer i bez udawania, że znamy globalną unikalność. Pokazuje nie „jesteś anonimowy w 37%”, lecz konkret: czy cztery wyniki matematyczne są stabilne i czy pasują do profili referencyjnych. Nie widzi warstwy sieciowej, historii ani pozostałych cech urządzenia, więc nie udaje pełnego fingerprintu.
Co faktycznie możesz zrobić
Nie ma jednego przełącznika „niewidzialny”. Możesz natomiast świadomie zmniejszać powierzchnię i utrudniać korelację:
- Blokuj znane trackery i third-party JavaScript. To nadal jedna z najskuteczniejszych warstw ochrony.
- Korzystaj z ochron wbudowanych w przeglądarkę. Tor Browser i Mullvad Browser stawiają na podobieństwo dużej grupy użytkowników. Brave stosuje kontrolowaną randomizację części wyników. Firefox i Safari ograniczają wybrane powierzchnie fingerprintingu.
- Nie dokładaj przypadkowych rozszerzeń spoofujących każdą właściwość osobno. Niespójna konfiguracja może stać się bardziej charakterystyczna niż domyślna.
- Rozdzielaj konteksty. Osobne profile lub przeglądarki ograniczają łączenie aktywności między rolami i kontami.
- Dobierz VPN albo Tor do rzeczywistego zagrożenia. Zmiana IP pomaga, ale nie zastępuje pozostałych warstw.
- Nie loguj się na konto, jeśli celem jest brak powiązania sesji z tożsamością. Najlepszy fingerprint nie jest potrzebny, gdy sam podajesz adres e-mail.
Najważniejsze: ochrona prywatności nie jest bezwartościowa tylko dlatego, że nie daje absolutnej anonimowości. Każdy zablokowany skrypt to jeden podmiot mniej, który może zebrać i połączyć dane.
Przeglądarka musi zdradzać szczegóły, żeby współczesny web działał
Problemem nie jest jedna zdradziecka właściwość JavaScriptu. Problemem jest suma szczegółów.
Przeglądarka musi wiedzieć, jak renderować fonty, używać GPU, odtwarzać audio, dopasować layout do ekranu, obsłużyć język, urządzenia wejścia i setki API. Każda różnica potrzebna do działania może zostać zamieniona w sygnał. Do tego dochodzi sieć, historia sesji i zachowanie człowieka.
Nie znaczy to, że każda strona zna twoje imię. Znaczy, że „nie mają mojego cookie” jest znacznie słabszym zdaniem, niż większość z nas chciałaby wierzyć.
FAQ
Czy fingerprint przeglądarki pozwala rozpoznać konkretną osobę?
Czy adblock blokuje fingerprinting?
Czy tryb incognito zmienia fingerprint przeglądarki?
Jak strony wykrywają Playwrighta i Selenium?
navigator.webdriver omija tylko najprostszy test; wiarygodne środowisko musi pozostać spójne na wszystkich warstwach.