Canvas-фингерпринтинг за пределами основ: почему два одинаковых Chrome рендерят разные пиксели
Два устройства с одинаковыми Chrome, ОС и GPU всё равно рендерят разные пиксели canvas. Почему конвейер рендеринга недетерминирован, почему инъекция шума ради приватности даёт обратный эффект и где место canvas в современном стеке детекции.
Canvas-фингерпринтинг применяется в продакшене с 2012 года. Идея проста: попросить браузер отрендерить 2D-контент, считать пиксели обратно и захешировать их. Разные устройства выдают слегка разные пиксели на одни и те же инструкции, и эти отличия достаточно стабильны, чтобы идентифицировать устройство между сессиями.
Это общеизвестно. Менее понятно другое — почему техника работает: почему одинаковые конфигурации ПО на одинаковом железе всё равно дают разный пиксельный вывод. Ответ лежит на пересечении драйверов GPU, рендеринга шрифтов и арифметики с плавающей точкой.
Конвейер рендеринга недетерминирован
Когда JavaScript вызывает context.fillText("Cwm fjord bank glyphs vext quiz", 4, 45), Chrome не рендерит пиксели сам. Он генерирует инструкции для Skia — библиотеки 2D-графики, — которая, в свою очередь, отдаёт команды рисования GPU-ускоренному бэкенду (обычно ANGLE на Windows, Metal на macOS или программному фолбэку в контейнерах).
На каждом шаге накапливаются небольшие различия реализации:
-
Шрифтовая подсистема Skia выбирает семейство шрифтов. Если Arial не установлен, происходит фолбэк — а фолбэк зависит от установленных в системе шрифтов.
-
Выбранный шрифт рендерится в запрошенном размере с помощью FreeType (на Linux), DirectWrite (на Windows) или Core Text (на macOS). У каждой библиотеки свои алгоритмы хинтинга.
-
Применяется субпиксельное позиционирование. Именно здесь идентичные шрифты начинают давать разные пиксели — алгоритм округляет позиции до дробных пиксельных смещений, и правила округления различаются между платформами.
-
Растеризованный текст композитится на canvas. Смешивание выполняется GPU-шейдерами на системах с аппаратным ускорением и CPU-кодом в режиме фолбэка.
Каждый из этих шагов может внести отличия в одно-два пиксельных значения. По отдельности незаметно. В совокупности уникально.
Версии драйверов GPU доминируют в фингерпринте
Две машины на Windows 11 с чипами Intel UHD Graphics 620 могут выдавать разный вывод canvas, если различаются их драйверы GPU. Intel выпускает обновления драйверов несколько раз в год, и каждое обновление может изменить субпиксельную фильтрацию, гамма-коррекцию и сглаживание текста.
Это значит, что стабильность canvas-фингерпринта ограничена частотой обновления драйверов. У пользователя, который обновляет графический драйвер — часто незаметно, через Windows Update, — canvas-фингерпринт изменится.
Системы детекции справляются с этим, трактуя canvas-фингерпринт как один из многих сигналов, а не как самостоятельный идентификатор. Когда он меняется, но остальные сигналы остаются стабильными (TLS-фингерпринт, WebGL-рендерер, часовой пояс, установленные шрифты), посетитель всё равно распознаётся.
Рендеринг эмодзи — золотая жила
Самый идентифицирующий элемент canvas-фингерпринта — часто не текст и не фигуры, а эмодзи. Рендеринг эмодзи зависит от шрифта эмодзи, поставляемого с ОС, — Segoe UI Emoji на Windows, Apple Color Emoji на macOS, Noto Color Emoji на Android.
Даже внутри одной ОС рендеринг эмодзи меняется между версиями. Как Windows 10 рендерит эмодзи радуги, отличается от того, как это делает Windows 11. У «улыбающегося лица» в iOS 16 сглаживание не такое, как в iOS 17.
Современные скрипты canvas-фингерпринтинга специально рендерят последовательности эмодзи, включая новые добавления в Unicode. Посетитель, заявляющий, что работает в Safari на iPhone, но не способный отрендерить эмодзи, добавленный в iOS 17, работает под более старой iOS — или лжёт о платформе.
Фреймворки автоматизации дают характерные фингерпринты
Headless Chrome, запущенный в Docker-контейнере без GPU, использует для рендеринга SwiftShader. SwiftShader выдаёт вывод canvas, который внутренне согласован, но отличается от любого Chrome с аппаратным ускорением.
Сигнатура узнаваема: необычно чистое сглаживание, конкретные значения цвета в областях градиентов и края текста, не совпадающие ни с одним известным драйвером GPU. Canvas-фингерпринт, совпадающий со SwiftShader в Chrome 124 в Linux-контейнере, — почти наверняка автоматизация: ни один реальный пользователь не запускает браузер таким образом.
Puppeteer-extra-stealth пытается подделать вывод canvas, перехватывая toDataURL() и возвращая изменённые данные. Но модификация часто вносит собственные артефакты — паттерны шума, повторяющиеся между сессиями, — что парадоксальным образом делает попытку анти-фингерпринтинга заметнее, чем полное бездействие.
Почему инъекция шума работает плохо
Ориентированные на приватность браузеры вроде Brave инъектируют шум в вывод canvas, чтобы предотвратить трекинг. Идея — добавить небольшие случайные вариации к значениям пикселей, чтобы одно и то же устройство выдавало разные фингерпринты в каждой сессии.
На практике у инъекции шума три проблемы:
1. Сам шум — это фингерпринт. Посетитель, у которого вывод canvas меняется при каждой загрузке, но остальные сигналы остаются идентичными, распознаётся как браузер, инъектирующий шум. Это само по себе идентифицирует.
2. Алгоритм шума стабилен. Реализация Brave выдаёт конкретные паттерны шума, которые не совпадают с естественной дисперсией рендеринга GPU. Системы детекции умеют отличать вывод canvas, который выглядит зашумлённым инъекцией, от того, что выглядит естественным.
3. Продвинутые системы комбинируют выборки. Несколько рендеров на одной странице или между сессиями можно усреднить. Если шум мал, среднее сходится к базовому детерминированному фингерпринту.
Что захватывает canvas-фингерпринтинг
Помимо очевидного визуального вывода, canvas-фингерпринтинг может извлечь несколько атрибутов устройства как побочные эффекты:
Время рендеринга — сколько времени canvas тратит на рендеринг, раскрывает возможности GPU. 4K-десктоп с дискретным GPU рендерит быстрее, чем ноутбук среднего класса.
Метрики текста — measureText() возвращает точные пиксельные ширины, которые зависят от рендеринга шрифтов. Ширину текста можно использовать как фингерпринт даже без рендеринга.
Поддержка операций композитинга — некоторые браузеры или конфигурации GPU поддерживают расширенные режимы композитинга. Проверка поддержки раскрывает границы возможностей.
Эффекты фильтров — фильтры в стиле CSS, применённые к canvas, дают разный вывод на разных GPU, особенно фильтры размытия и тени.
Где место canvas-фингерпринтинга в стеке детекции
Сам по себе canvas-фингерпринтинг — не надёжный идентификатор. Драйверы меняются. Пользователи переключают браузеры. Инъекция шума распространена среди аудитории, ориентированной на приватность.
Его настоящая сила — в перекрёстной проверке. Когда сессия заявляет, что это вернувшийся пользователь на основании cookies или входа в аккаунт, canvas-фингерпринт выступает вторым сигналом: совпадение с историческим фингерпринтом подтверждает заявление, несовпадение — сигнал захвата аккаунта или угона сессии.
Для bot detection canvas-фингерпринтинг отлично ловит контейнеризированную автоматизацию. У реальных пользователей — реальные GPU. Реальные GPU выдают узнаваемые сигнатуры рендеринга. Всё, что рендерит как SwiftShader или как плохо подделанный canvas, попадает в корзину «почти наверняка бот».
Техника 2012 года всё ещё работает в 2026-м по той же причине: рендеринг детерминирован в рамках окружения, но почти невозможен для полной подделки. Каждая попытка спрятаться оставляет следы самой попытки.