Что такое браузерный фингерпринтинг?
Браузерный фингерпринтинг — техника отслеживания, которая опознаёт пользователей по сведениям о конфигурации браузера и устройства. В отличие от куки, которые лежат на вашем устройстве и удаляются, отпечаток вычисляется из данных, которые браузер раскрывает по умолчанию — из сведений, существующих потому, что без них сайты не смогут нормально работать.
Браузер сообщает операционную систему, разрешение экрана, часовой пояс, язык, установленные шрифты, модель видеокарты, особенности обработки звука и многое другое. По отдельности ни один из этих параметров вас не опознаёт. Вместе они дают подпись, которая с подавляющей вероятностью уникальна именно для вашего браузера на вашем устройстве.
Исследование INRIA 2020 года показало, что более 94 % браузеров с включённым JavaScript имели уникальный отпечаток. С тех пор точность только выросла: компании, занимающиеся обнаружением, добавили новые сигналы и улучшили алгоритмы хеширования.
Сигналы отпечатка
В отпечаток браузера вносят вклад более 50 отдельных сигналов. Вот самые важные:
Браузер рисует невидимый текст и фигуры на элементе canvas HTML5, а затем считывает пиксели обратно. Разные видеокарты, драйверы, движки отрисовки шрифтов и настройки сглаживания в ОС дают чуть различающийся результат — достаточно, чтобы получить почти уникальный хеш.
Похоже на canvas, но с 3D-отрисовкой. Браузер рендерит трёхмерную сцену, и результат зависит от видеокарты, версии драйвера и реализации OpenGL. Заодно раскрываются строки вендора и рендерера GPU (например, «ANGLE (NVIDIA GeForce RTX 3080)»).
Браузер генерирует аудиосигнал через Web Audio API и измеряет, как он обрабатывается. Разное звуковое железо и драйверы дают разные значения с плавающей точкой — так получается уникальный аудиоотпечаток, и при этом ничего не звучит.
Браузер измеряет ширину и высоту отрисованного текста в разных шрифтах. Проверяя, какие шрифты установлены (и как они отрисовываются), скрипт фингерпринтинга определяет именно ваш набор шрифтов — а он заметно отличается от машины к машине.
Разрешение экрана, доступная область (без панели задач), глубина цвета, device pixel ratio, внутренние размеры окна и то, развёрнуто ли оно. Сочетание этих значений заметно сужает круг.
Строка user agent, платформа, язык (языки), число ядер процессора (hardwareConcurrency), объём памяти устройства, максимальное число точек касания, тип соединения и включён ли «Do Not Track». Каждое добавляет по нескольку бит энтропии.
Помимо этих основных сигналов, скрипты фингерпринтинга собирают ещё:
- Часовой пояс и локаль —
Intl.DateTimeFormat().resolvedOptions().timeZoneвыдаёт ваш точный часовой пояс, а настройки локали — предпочтения форматирования. - Перечисление плагинов и MIME-типов — в современных браузерах менее полезно, но по-прежнему проверяется.
- Утечка локального IP через WebRTC — WebRTC может раскрыть адреса локальной сети даже за VPN, и это используется как дополнительный идентификатор.
- Battery API — в браузерах, где он ещё поддерживается, точный уровень заряда и состояние зарядки добавляют энтропии.
- Отпечаток ClientRects — точное измерение прямоугольников отрисованных элементов DOM, которые зависят от отрисовки шрифтов и движка вёрстки.
- Голоса синтеза речи — список доступных голосов text-to-speech различается в зависимости от ОС и установленных языковых пакетов.
- Точность математики — результаты
Math.tan(),Math.atan2()и других тригонометрических функций слегка различаются между архитектурами процессоров и движками JS.
Как вычисляется отпечаток
Скрипт фингерпринтинга собирает все эти значения, сериализует их в строку или структурированный объект и прогоняет через хеш-функцию (обычно MurmurHash3 или SHA-256), получая компактный идентификатор. Вот упрощённый пример:
const signals = {
canvas: getCanvasHash(),
webgl: getWebGLHash(),
audio: getAudioHash(),
fonts: getInstalledFonts(),
screen: `${screen.width}x${screen.height}x${screen.colorDepth}`,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
language: navigator.language,
cores: navigator.hardwareConcurrency,
memory: navigator.deviceMemory,
platform: navigator.platform,
userAgent: navigator.userAgent,
touchPoints: navigator.maxTouchPoints,
// ... 30+ more signals
};
const fingerprint = murmurhash3(JSON.stringify(signals));
Полученный хеш — короткая строка вроде a3f8c91b — служит уникальным ID вашего браузера. Он хранится на сервере и сравнивается с последующими визитами.
Энтропия: почему одни сигналы важнее других
Не все сигналы одинаково влияют на уникальность. Понятие энтропии измеряет, сколько опознавательной информации несёт сигнал. Больше энтропии — больше бит информации, то есть сигнал сильнее сужает вашу личность.
| Сигнал | Примерная энтропия | Влияние на уникальность |
|---|---|---|
| Хеш canvas | ~14 бит | Очень высокое — различия GPU и отрисовки |
| Рендерер WebGL + хеш | ~12 бит | Очень высокое — определяет модель GPU |
| Установленные шрифты | ~11 бит | Очень высокое — своё на каждой машине |
| Хеш AudioContext | ~10 бит | Высокое — зависит от железа |
| Разрешение экрана | ~5 бит | Среднее — многие разрешения распространены |
| Часовой пояс | ~4 бита | Среднее — около 24 крупных групп поясов |
| User agent | ~6 бит | Среднее — версия браузера добавляет конкретики |
| Язык | ~3 бита | Низкое — ограниченный набор распространённых языков |
| Платформа | ~2 бита | Низкое — Win/Mac/Linux/мобильные |
| Глубина цвета | ~1 бит | Очень низкое — почти у всех 24 бита |
Суммарная энтропия по всем сигналам современного скрипта фингерпринтинга обычно превышает 40 бит. При 40 битах энтропии возможных комбинаций больше триллиона — более чем достаточно, чтобы однозначно опознать каждый браузер на Земле.
Активный и пассивный фингерпринтинг
Техники фингерпринтинга делятся на две категории:
Пассивный фингерпринтинг использует данные, которые браузер шлёт автоматически с каждым HTTP-запросом: заголовок user agent, принимаемые языки и кодировки, характеристики соединения. Серверу не нужно выполнять JavaScript. Это незаметнее, но сигналов меньше.
Активный фингерпринтинг выполняет JavaScript на странице и «прощупывает» браузер: рисует изображения на canvas, опрашивает WebGL, измеряет аудиовыход, перечисляет шрифты и проверяет поведение отдельных API. Он гораздо мощнее, и на нём держится большинство систем обнаружения.
На практике системы обнаружения используют оба. Пассивный отпечаток из HTTP-заголовков считается на сервере, активный — на клиенте через JavaScript и отправляется обратно как сигнал. Затем их объединяют и сверяют друг с другом на непротиворечивость.
Как системы обнаружения используют отпечатки
Отпечатки браузера не используют в отрыве от прочего. Современные платформы обнаружения объединяют их с другими сигналами в оценку риска:
- Репутация IP — принадлежит ли адрес дата-центру, VPN или домашнему подключению? Попадался ли он в жалобах на злоупотребления?
- Поведенческая биометрия — траектории мыши, скорость прокрутки, ритм набора, тайминг кликов. У конкретного человека они на удивление постоянны и крайне трудно подделываются.
- Стабильность в рамках сессии — остаётся ли отпечаток неизменным между загрузками страниц? У настоящих браузеров отпечатки стабильны. Меняющиеся от загрузки к загрузке помечаются как подменённые.
- Согласованность сигналов между собой — рассказывают ли все сигналы одну и ту же историю? Если user agent говорит «Chrome под Windows», а рендерер WebGL — видеокарта, существующая только в macOS, отпечаток противоречив.
- Следы автоматизации — выставлен ли
navigator.webdriver? Есть ли характерные свойства, оставленные Selenium, Puppeteer или Playwright? Обнаруживаются ли Chrome DevTools?
Сам по себе отпечаток — лишь один из входных параметров. По-настоящему важна согласованность и правдоподобие всего набора сигналов. Идеальный отпечаток с IP дата-центра и роботизированными движениями мыши всё равно будет помечен.
Почему обычные средства приватности не работают
Люди часто хватаются за средства приватности, бесполезные против фингерпринтинга:
Инкогнито / приватный просмотр
Режим инкогнито удаляет куки и историю при закрытии окна. С отпечатком он не делает ничего. Хеш canvas, вывод WebGL, шрифты, разрешение экрана и все прочие сигналы остаются такими же, как в обычной сессии. Тот же отпечаток, просто без куки.
VPN / Tor
VPN меняет ваш IP-адрес. Tor через Tor Browser вдобавок унифицирует часть свойств браузера. Но обычные VPN отпечаток браузера вообще не меняют — хеш canvas и рендерер WebGL остаются прежними. А Tor Browser, хоть и сопротивляется фингерпринтингу, узнаётся именно как Tor Browser, и многие площадки его просто блокируют.
Расширения браузера
Расширения вроде Canvas Blocker или дополнений для подмены отпечатка пытаются перехватывать API фингерпринтинга. Проблема тройная: (1) расширения выявляются по побочным эффектам в DOM и поведении API, (2) они не могут последовательно подменять сигналы, рождающиеся ниже уровня JavaScript, и (3) наличие конкретных приватных расширений само по себе является сигналом отпечатка — у реальных пользователей они стоят крайне редко.
Несколько профилей браузера (Chrome/Firefox)
В Chrome и Firefox есть несколько пользовательских профилей, у каждого свои куки и история. Но у всех у них один и тот же отпечаток, потому что это один браузер на одном железе. Профили разные, а хеш canvas, WebGL и шрифты одинаковые. Системы обнаружения связывают их мгновенно.
Как антидетект-браузеры работают с отпечатками
Антидетект-браузеры создаются специально для того, чтобы бить по фингерпринтингу в его источнике. Вот что делает хорошо сделанный браузер иначе:
Конфигурация отпечатка для каждого профиля. Каждый профиль получает собственную полную конфигурацию: свой сид для шума canvas, подменённый рендерер WebGL, соответствующий реальной видеокарте, набор шрифтов, согласованный с выбранной ОС, параметры экрана реального устройства и так далее. Ключевое — чтобы все эти значения были согласованы между собой.
Правка на уровне движка. В правильно сделанном антидетект-браузере изменения отпечатка происходят внутри движка — на уровне C++ в Chromium. Когда сайт вызывает canvas.toDataURL(), сам движок отдаёт изменённые пиксельные данные. Обнаруживать нечего: подмены в JavaScript нет, потому что изменение происходит до того, как данные доходят до слоя JavaScript.
Внесение шума со стабильными сидами. Вместо случайных значений (которые менялись бы при каждой загрузке страницы и вызывали подозрение) хорошие антидетект-браузеры используют детерминированный шум на основе сида, своего для каждого профиля. Один и тот же тест canvas внутри профиля всегда даёт одинаковый хеш, а разные профили — разные. Именно так ведут себя настоящие браузеры: ваш отпечаток стабилен от сессии к сессии.
Проверка правдоподобия железа. Перед созданием профиля система проверяет, правдоподобно ли сочетание сигналов. Профиль, заявляющий Windows 10 с видеокартой Apple M2, вызвал бы подозрение сразу. Генератор отпечатков следит, чтобы ОС, GPU, шрифты, user agent и прочие сигналы складывались в непротиворечивую личность.
Цель — не сделать браузер, который выглядит «анонимным». Цель — сделать браузер, который выглядит как обычный человек за обычным компьютером, только для каждого профиля это другой обычный человек.
Гонка вооружений
Браузерный фингерпринтинг — это гонка вооружений между компаниями по обнаружению и антидетект-инструментами, и она ускоряется.
Системы обнаружения становятся изощрённее. Они добавляют новые сигналы (например, измерения getClientRects() и поведение WebCodecs API), сопоставляют поведенческую биометрию с данными отпечатка и применяют машинное обучение, чтобы находить «невозможные» или «маловероятные» конфигурации, которых ни один настоящий браузер не выдаст.
Одновременно антидетект-браузеры отвечают правкой более глубоких слоёв движка, улучшением проверок согласованности и созданием более полных баз реальных конфигураций устройств, используемых как шаблоны.
Ясно одно: фингерпринтинг никуда не денется (он слишком полезен для борьбы с мошенничеством и таргетинга рекламы), а пассивные средства вроде VPN и расширений с ним не справляются. Если изоляция на уровне отпечатка важна для вашей работы, нужен инструмент, созданный именно для этого.