CreepJS не читает одно поддельное значение — он сверяет доверенные и недоверенные источники, вылавливая несоответствия, которые почти невозможно скрыть.
Большинство инструментов фингерпринтинга отвечают на один вопрос: насколько уникален этот браузер? CreepJS, открытый исследовательский проект по фингерпринтингу, задаёт другой: говорит ли этот браузер правду? Вместо того чтобы просто снять хеш Canvas или строку WebGL-рендерера и двигаться дальше, он сверяет каждый сигнал со всеми остальными сигналами, которые выдаёт тот же браузер, — а также с тем, как должна вести себя настоящая реализация этого сигнала. Когда значение выглядит правдоподобно само по себе, но противоречит чему-то ещё, что раскрывает браузер, CreepJS называет это ложью, и балл доверия соответственно падает. Эта статья объясняет модель обнаружения лжи в общих чертах: что значат «доверенные» и «недоверенные» источники, как патчинг прототипа или геттера выдаёт себя сам, и почему один и тот же класс проверок ловит и наивную подмену user-agent, и антидетект-браузеры.
Ключевые выводы
- Ключевая идея CreepJS — согласованность, а не идентичность: инструменту не нужна база «правильных» значений, достаточно доказать, что собственные сигналы браузера противоречат друг другу или тому, как ведёт себя настоящая реализация.
- Он сравнивает доверенные и недоверенные источники одного и того же факта — прямое чтение свойства против косвенного вывода, или результат на главном потоке против того же вычисления, выполненного в Worker, — и отмечает расхождение между ними.
- Патчинг прототипов и геттеров — стандартный способ, которым инструменты подмены переопределяют такие значения, как
navigator.userAgentилиHTMLCanvasElement.prototype.toDataURL, — оставляет структурные улики: изменённые дескрипторы свойств, вывод.toString(), переставший совпадать с нативным кодом, и ссылочную идентичность, которая меняется от вызова к вызову. - Proxy-ловушки, используемые для перехвата доступа к свойствам, обнаруживаются сами по себе, потому что настоящему API браузера не нужна обёртка
Proxy, чтобы ответить на запрос. - Та же логика обнаружения лжи объясняет, почему наивная подмена user-agent или Canvas — и даже хорошо сделанные антидетект-браузеры — обычно оставляют паттерн противоречий, а не одну очевидную улику.
Что на самом деле проверяет CreepJS
Заявленная цель CreepJS, согласно его репозиторию на GitHub, — «пролить свет на слабые места и утечки приватности в современных расширениях и браузерах, противодействующих фингерпринтингу». Он запускает широкий набор проверок — рендеринг canvas и WebGL, доступность аудио и шрифтов, вычисленные CSS-стили, данные об экране и часовом поясе, характеристики JavaScript-рантайма и многое другое — и вместо того чтобы просто хешировать результаты в единый идентификатор, оценивает каждый на внутреннюю правдоподобность. На выходе получается балл доверия вместе с постатейной разбивкой: какие сигналы выглядят нативными, а какие — собранными, пропатченными или иначе несовместимыми с настоящим экземпляром браузера.
Эта постановка вопроса важна для тех, кто проверяет собственную настройку. Инструмент уникальности отпечатка говорит, насколько вы узнаваемы. Инструмент обнаружения лжи говорит, держится ли вообще та личность, которую вы предъявляете, — а это ближе к тому, что в продакшене проверяют системы борьбы с ботами, оценки мошенничества и обнаружения антидетект-браузеров, как описано в статье как сайты обнаруживают антидетект-браузеры и подмену отпечатков.
Модель обнаружения лжи: согласованность, а не идентичность
Наивная проверка фингерпринтинга читает значение и записывает его. Проверка на ложь читает один и тот же факт несколькими путями и сравнивает ответы. Если браузер действительно то, чем себя называет, все пути к одному факту должны совпадать, потому что в конечном счёте все они происходят из одного движка, одного конвейера рендеринга и одной операционной системы. Если что-то пропатчено, чтобы выдавать ложное значение, крайне трудно согласованно пропатчить каждый путь — и именно на пропущенных путях всплывает ложь.
Это тот же принцип, что лежит в основе проверки согласованности отпечатка в целом: уникальность измеряет, насколько редок отпечаток, а согласованность — насколько правдоподобно он мог бы принадлежать одному реальному устройству. CreepJS применяет ту же идею согласованности на более тонком уровне — не просто «совпадает ли GPU с заявленной ОС», а «совпадает ли низкоуровневое поведение конкретного API с тем, что производит настоящая реализация этого API», и так повторяет проверку в десятках отдельных случаев.
Доверенные и недоверенные источники: где ловится ложь
Полезный способ думать об этом классе инструментов — в терминах доверенных и недоверенных источников одного и того же лежащего в основе факта. Доверенный источник — это тот, который скрипту подмены дорого или неудобно перехватывать: значение, считанное из другого контекста исполнения, вычисленное как побочный эффект несвязанного кода, или выведенное из поведения, а не из свойства, которое скрипт может просто переопределить. Недоверенный источник — это то самое очевидное, часто подделываемое свойство: navigator.userAgent, вызов toDataURL() у Canvas, параметр WebGL — всё то, что уже умеет перехватывать любой антидетект-инструмент и расширение для приватности.
Проверка звучит не как «реально ли недоверенное значение» само по себе — а как «согласуется ли недоверенное значение с доверенным». Несколько примеров того, как это обычно выглядит:
- Главный поток против контекста Worker. Одно и то же вычисление — отрисовка Canvas, чтение часового пояса, математическая операция — можно выполнить и на главном потоке, и внутри Web Worker. Настоящий браузер даёт одинаковый результат в обоих местах, потому что оба контекста исполнения используют один и тот же движок. Инструменты подмены часто патчат только глобальный объект главного потока и упускают отдельную глобальную область Worker, из-за чего два контекста расходятся. Проверка межконтекстной согласованности Canvas — сравнение вывода
OffscreenCanvasв Worker с canvas, прикреплённым к документу, — это частный случай этого общего паттерна. - Прямое чтение против выведенного значения. Некоторые факты можно прочитать напрямую из свойства и также вывести косвенно — из побочного эффекта, сообщения об ошибке или элемента поведения, который правильно воспроизведёт только настоящая реализация. Если прямое чтение и выведенное значение расходятся, правду может говорить только одно из них, и почти никогда не то, которое проще всего пропатчить.
- Заявленная идентичность против наблюдаемого поведения. Строка user-agent заявляет браузер и движок; тайминги JavaScript-движка, формулировки сообщений об ошибках и наличие функций раскрывают, какой движок работает на самом деле, независимо от того, что говорит строка, — именно этот механизм разобран в статье как обнаружить подмену user-agent.
Ни одна из этих проверок не требует, чтобы CreepJS знал, каким должен быть ваш «правильный» отпечаток. Ему нужны лишь два независимых окна на один и тот же факт и уверенность, что настоящий браузер не может не показать одинаковый ответ через оба.
Дескрипторы свойств, геттеры и Proxy-ловушки
Помимо сравнения источников, методология CreepJS включает прямое обнаружение патчинга прототипов — поиск структурных улик того, что API браузера было переопределено, а не просто проверку правдоподобности возвращаемого значения.
Подмена такого значения, как navigator.userAgent или метода Canvas, обычно означает переопределение геттера или переприсваивание метода прототипа. Это вмешательство меняет не только само значение:
- Дескрипторы свойств.
Object.getOwnPropertyDescriptor()на нативном методе возвращает конкретную, предсказуемую форму и.toString(), равный"function name() { [native code] }". Наивно пропатченная версия часто возвращает видимое тело функции вместо этого, или override, забывший синтезировать маркер нативного кода. - Proxy-ловушки. Перехват доступа к свойству через
Proxy— распространённый способ динамически патчить геттеры, но живой Proxy ведёт себя иначе при проверках на ссылочное равенство, чем нативное свойство, — обращение к нему дважды может вернуть различимые экземпляры обёртки, а его ловушкиget/hasреагируют на зонды, на которые нативному свойству никогда не приходится отвечать. - Ссылочная идентичность. Настоящий метод прототипа — это в точности один и тот же объект-функция при каждом обращении. Пропатченная версия, реализованная как свежее замыкание или сгенерированная обёртка, может не пройти сравнение
===при повторных обращениях — то, чего нативная реализация не делает никогда.
Ничто из этого не доказывает, каким должно было быть настоящее значение, — только то, что свойство, отвечающее на запрос, не то, с которым браузер был поставлен изначально. Это зачастую более сильный сигнал, чем попытка угадать, «выглядит ли правильно» конкретный хеш Canvas или строка GPU, потому что он вообще не зависит от базы известных правильных значений.
Почему наивная подмена и антидетект-браузеры всё равно проваливаются
Общая тема всех этих проверок в том, что подделать один сигнал легко, а согласованно подделать весь целостный набор доверенных и недоверенных источников — нет. Скрипт, переопределяющий navigator.userAgent, редко также патчит эквивалент в контексте Worker, корректирует каждый нижестоящий дескриптор свойства и воспроизводит нативный вывод .toString() для только что установленного геттера. Антидетект-браузеры — специально построенные форки Chromium, призванные согласованно подделывать значения по всему профилю, — подбираются ближе, но даже они склонны оставлять тот же класс шва: значение, пропатченное на главном потоке, но не внутри Worker, Proxy-ловушку, отвечающую чуть иначе, чем нативное свойство, или заявленную идентичность, которую тихо опровергает проверка доверенного источника.
Это вечнозелёная модель: конкретные проверки, которые запускает исследовательский инструмент фингерпринтинга, будут расширяться по мере эволюции движков и техник подмены, но лежащая в основе модель — прочитать один и тот же факт доверенным и недоверенным путём и трактовать расхождение как улику — не устаревает. Это та же логика, что делает обнаружение на основе согласованности устойчивым к разовым трюкам: детектору не нужно перечислять каждую возможную ложь, достаточно продолжать умножать число независимых путей, которые инструмент подмены обязан пропатчить одинаково.
Проверьте свой браузер
Собственные инструменты BrowserInsight применяют то же мышление, ориентированное на согласованность, не требуя от вас разбираться в исследовательской панели фингерпринтинга. Проверка отпечатка показывает сигналы Canvas, WebGL и шрифтов вместе с проверками согласованности, которые отмечают, когда они не сходятся; инструмент обнаружения ботов запускает те проверки на автоматизацию и согласованность, которые система борьбы с мошенничеством выполнила бы в продакшене. Если вам нужна пошаговая версия той же самопроверки, статья «Согласованность отпечатка браузера: чек-лист самопроверки» проводит через семь конкретных проверок UA/GPU/часового пояса/шрифтов, которые можно выполнить за пару минут. Ничто из этого не руководство по тому, как пройти CreepJS или любой другой инструмент обнаружения лжи, — смысл понимания этой модели в том, чтобы увидеть, что реально проверяется именно согласованность, а не какое-то одно значение.
Часто задаваемые вопросы
Собирает ли CreepJS мой отпечаток и хранит ли его?
CreepJS — это клиентский исследовательский инструмент, который вы запускаете в собственном браузере, чтобы увидеть, что он о вас раскрывает; смысл запускать его самостоятельно — проверить собственные сигналы, а не быть отслеженным им. Всегда проверяйте политику приватности конкретного инструмента, прежде чем полагаться на любой размещённый онлайн сервис фингерпринтинга.
Может ли хорошо сделанный антидетект-браузер обмануть инструмент обнаружения лжи вроде CreepJS?
Против части проверок — иногда. Согласованно скоординировать каждый доверенный и недоверенный источник — главный поток и контекст Worker, каждый дескриптор свойства, каждый выведенный побочный эффект — на фоне постоянно расширяющегося набора проверок трудно поддерживать долго, поэтому антидетект-настройки, как правило, оставляют хотя бы один шов, даже если построены тщательно, как обсуждается в статье как сайты обнаруживают антидетект-браузеры и подмену отпечатков.
Означает ли низкий балл доверия, что реальный пользователь — бот или мошенник?
Нет. Балл обнаружения лжи измеряет согласованность отпечатка, а не намерение. Расширения для приватности, старые сборки браузеров и необычные, но легитимные конфигурации могут сработать на отдельных проверках. Продакшен-системы сочетают этот сигнал с другими, а не рассматривают его как самостоятельный вердикт.
Чем обнаружение лжи отличается от обычной проверки уникальности отпечатка?
Проверка уникальности (хорошо известный пример — Cover Your Tracks от EFF) измеряет, насколько узнаваем ваш совокупный отпечаток среди других посетителей. Обнаружение лжи вместо этого спрашивает, согласованы ли отдельные сигналы, составляющие этот отпечаток, друг с другом и с поведением настоящего браузера, — совсем другой вопрос, который вообще не требует сравнения вас с чьим-то ещё отпечатком.


