Назад в Центр знаний
Отпечатки

Детект безголового режима в CreepJS: три отдельных вердикта

Один зонд по CreepJS: мягкие сигналы, жёсткие биты и проверки обёрток оцениваются отдельно, поэтому очищенная страница всё равно сообщает процент.

BotBrowser Team

Документация

Нужна структурированная документация по теме Отпечатки?

Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.

BotBrowser позволяет воспроизвести авторизованный фикстур того же источника под объявленным профилем и сохранить ограниченную запись того, что страница сообщила о своей среде, но он не может сделать тонкую поверхность толстой, не может превратить восстановленную поверхность в сигнал, который никогда не проверялся, и не может изменить то, как библиотека группирует собственные находки. CreepJS читается здесь по открытому исходному коду, три группы вердикта, которые он вычисляет, воспроизводятся, и фиксируется, сколько стоит один сигнал в каждой группе, сколько мягких сигналов нужно странице, чтобы мягкая группа читалась как безголовая, какие жёсткие биты переживают очистку очевидных, и почему процент одной группы нельзя сравнивать с процентом другой.

Кратко

  • CreepJS сообщает безголовый режим тремя отдельными процентами, а не одним: likeHeadless по 16 мягким сигналам, headless по 3 жёстким битам и группа обёрток по 5 проверкам.
  • Рабочий стол с головой и настоящим менеджером окон получает likeHeadless=0% headless=0% wrapper=0%.
  • Классический безголовый профиль доходит до likeHeadless=44% (7/16), тогда как его жёсткая группа остаётся на 0%, потому что мягкие сигналы и жёсткие биты измеряют разное.
  • Драйвер, который выдаёт свой флаг и свой токен, получает headless=100% (3/3), единственная группа с полным результатом в этом прогоне.
  • Один сигнал стоит 6% в мягкой группе, 33% в жёсткой и 20% в группе обёрток, поэтому одна и та же находка весит совершенно по-разному.
  • Страница, восстановившая объект chrome, плагины, типы MIME, данные user agent и просмотрщик PDF, всё равно сообщает likeHeadless=13% (2/16), потому что геометрия экрана не тронута.
  • Задание правдоподобного размера окна снижает это до likeHeadless=6% (1/16), и последним мягким сигналом остаётся программный рендерер.
  • Обёртка, которая смещает объект chrome, ломает его runtime и меняет Function.prototype.toString, получает wrapper=60% (3/5), а две другие группы остаются нулевыми.
  • Сигнал noTaskbar это просто равенство геометрии, поэтому развёрнутое окно на настоящем рабочем столе тоже его вызывает.

Содержание

Первая панель показывает три отдельных группы процентов: шестнадцать мягких сигналов, три жёстких бита и пять проверок обёрток. Вторая показывает классический безголовый профиль с 44% в мягкой группе, тогда как жёсткая остаётся нулевой. Третья показывает драйвер со 100% в жёсткой группе. Четвёртая показывает очищенную страницу, которая всё ещё сообщает 13%, и затем 6% после задания размера окна, рядом с обёрткой, получившей 60% только в своей группе.

Почему три группы разделены

Детект безголового режима часто описывают как один вопрос с одним ответом, а CreepJS намеренно сообщает три ответа. Группы из одного модуля и измеряют разные виды доказательств:

headed-desktop -> likeHeadless=0% (0/16) headless=0% (0/3) wrapper=0% (0/5)

Первая группа содержит шестнадцать мягких сигналов: нет объекта chrome, нет плагинов, нет типов MIME, пустая платформа client hints, отключённый просмотрщик PDF, экран без панели задач, область просмотра равна экрану, программный рендерер и несколько пробелов в возможностях. Каждый это поверхность, которая выглядит тонкой, и каждая также может быть у законного развёртывания.

Флаг драйвера в этой группе это то же значение, которое читает детектор подмены, когда проверяет, сообщает ли нативная функция всё ещё о себе, и подменённые нативные функции и что видит список toString это фиксирует. Вторая группа содержит три жёстких бита, и они жёсткие в другом смысле: флаг драйвера, токен HeadlessChrome в user agent и тот же токен внутри Worker. Они описывают не тонкую поверхность, а браузер, который сам о себе объявил.

Строка решения, построенная из нескольких групп, оценивается и фильтруется иначе, чем эти три процента, и почему один странный сигнал не выносит вердикт это фиксирует. Третья группа содержит пять проверок обёрток: обнаруживается ли прокси iframe, стоит ли объект chrome в неправдоподобной позиции в порядке ключей window, ведёт ли себя chrome.runtime как настоящий объект, изменён ли Function.prototype.toString, и расходятся ли GPU главного потока и Worker. Они описывают страницу, которая отредактировала свою среду, а не ту, что просто выглядит тонкой.

Три группы, три вида утверждений. Слить их в одно число значит потерять различие между «эта среда минимальна», «этот браузер сказал, что он автоматизирован» и «эта страница отредактировала себя», а эти три случая требуют совершенно разных действий.

Сколько стоит один сигнал в каждой группе

Проценты несравнимы между группами, потому что каждый это доля своего знаменателя. Зонд указывает вес одной находки в каждой:

weight:likeHeadless -> one signal = 6%
weight:headless -> one signal = 33%
weight:wrapper -> one signal = 20%

Один мягкий сигнал сдвигает мягкую группу на шесть пунктов, тогда как один жёсткий бит сдвигает жёсткую на тридцать три. Отчёт, печатающий «44%» и «33%» рядом, приглашает читать их как одну шкалу, а это не так: 44% мягкой группы это семь тонких поверхностей, а 33% жёсткой это один браузер, который объявил о себе.

Мягкой группе к тому же нужно много сигналов, прежде чем она прочтётся как преимущественно безголовая:

likeHeadless:1-of-16 -> 6%
likeHeadless:4-of-16 -> 25%
likeHeadless:8-of-16 -> 50%
likeHeadless:12-of-16 -> 75%
likeHeadless:16-of-16 -> 100%

Половина группы требует восьми тонких поверхностей сразу. Это высокая планка, чтобы пересечь её случайно, и поэтому мягкий процент описывает, насколько минимальна среда, а не вероятность того, что она безголовая.

Строка сравнения говорит то же с другой стороны:

compare -> all-16-soft=100% vs one-hard=33% (groups are reported separately)

Страница со всеми шестнадцатью мягкими сигналами читается как 100% в своей группе, а страница с одним жёстким битом как 33% в своей. Ни одно не больше в сколько-нибудь осмысленном смысле, потому что они отвечают на разные вопросы. Кто сортирует два процента, сортирует две разные единицы.

Какие сигналы переживают очищенную страницу

Интересно то, что происходит, когда страница приводит в порядок очевидные сигналы. Зонд подаёт состояние, где объект chrome, плагины, типы MIME, платформа client hints и просмотрщик PDF восстановлены, а геометрия экрана осталась как была:

cleaned-obvious-tells -> likeHeadless=13% (2/16) headless=0% (0/3) wrapper=0% (0/5)
cleaned-plus-window -> likeHeadless=6% (1/16) headless=0% (0/3) wrapper=0% (0/5)

Восстановление пяти поверхностей всё ещё оставляет тринадцать процентов, потому что два сигнала геометрии никто не трогал. Задание правдоподобного размера окна снижает это до шести, и последним мягким сигналом остаётся программный рендерер. Жёсткая группа и группа обёрток всё время на нуле, и это полезная часть: очистка поверхностей не создаёт находок обёрток, она только убирает мягкие.

Эта асимметрия важна для того, кто читает отчёт после изменения. Страница, ушедшая с сорока четырёх на шесть процентов, не стала более или менее безголовой; она стала менее минимальной. Жёсткая группа, та самая, что описывает браузер, объявивший о себе, вообще не сдвинулась, и кто смотрит только на итог, решит, что что-то улучшилось, хотя лежащее в основе утверждение не изменилось.

Обратное тоже верно. Группа обёрток может вырасти без единого мягкого сигнала:

wrapper-wrapped -> likeHeadless=0% (0/16) headless=0% (0/3) wrapper=60% (3/5)

Страница, которая держит каждую поверхность толстой, но смещает объект chrome, ломает его runtime и меняет Function.prototype.toString, получает шестьдесят процентов в группе обёрток, тогда как две другие пусты. Это совершенно иная находка, чем тонкая среда, и она видна только потому, что группы разделены.

Где мягкие сигналы преувеличивают случай

Несколько мягких сигналов это обычные условия, которые законное развёртывание тоже создаёт, и зонд фиксирует два из них.

Сигнал прав требует согласия двух независимых условий:

permissions-bug -> prompt+denied=true prompt+default=false granted+denied=false

Он срабатывает только когда запрос прав сообщает prompt, а разрешение на уведомления равно denied. Ни одно из условий само по себе его не вызывает, и это намеренное сужение: браузер, который просто запрещает уведомления политикой, не помечается, и только несогласованная пара даёт срабатывание.

Сигнал панели задач это противоположный случай, и он самый слабый в группе:

no-taskbar -> 1080/1080+1920/1920=true 1080/1040+1920/1920=false

Это просто равенство между экраном и его доступной областью, поэтому любое развёрнутое окно на настоящем рабочем столе со скрытой панелью его вызывает, как и киоск или полноэкранное устройство. Один пункт мягкой группы сам по себе стоит немного, а этот стоит даже меньше, чем намекают его шесть процентов.

Урок для развёртывания о том, на какие поверхности тратить усилия. Восстановление плагинов и типов MIME двигает мягкую группу, но геометрия и имя рендерера это то, что остаётся после приведения в порядок всего остального. Профиль, который верно делает лёгкие поверхности и не трогает геометрию, читается как шесть процентов мягких, и кто не знает размеров групп, прочтёт это как «почти ничего», хотя на деле это один конкретный сигнал.

Запись одного безголового показания

Безголовое показание подтверждает узкое утверждение: эта версия библиотеки, эти состояния сигналов дали эти три процента. Четыре поля держат его проверяемым:

  1. Коммит библиотеки и три списка сигналов, потому что список, получивший или потерявший ключ, меняет каждый процент без всякого изменения на странице.
  2. Имена сработавших сигналов по группам, потому что шесть и тридцать три процента описывают разные виды доказательств, а число скрывает какие.
  3. Знаменатели каждой группы, потому что сравнивающий мягкий процент с жёстким сравнивает две разные единицы.
  4. Сигналы, которые не оценивались, потому что нескольким нужна живая страница, а статическое воспроизведение подаёт их как входные данные.

Публичные источники

Практический вывод

Читайте три процента как три отдельные находки, а не как одну оценку, потому что мягкий процент описывает, насколько минимальна среда, жёсткий описывает браузер, объявивший о себе, а процент обёрток описывает страницу, отредактировавшую себя. Воспринимайте падение с сорока четырёх до шести процентов как изменение того, сколько среды раскрыто, а не того, безголовый ли браузер, поскольку жёсткая группа не сдвинулась. Когда отчёт показывает рост одной группы при пустых остальных, ищите проверки обёрток, а не отсутствующие поверхности. О том, как библиотека сворачивает результаты групп в одно упорядоченное решение, читайте группы сигналов CreepJS и их объединение; о том, что может увидеть одна проверка исходного текста, читайте подменённые нативные функции и что видит список toString.

#CreepJS#Детект Безголового Режима#Сигналы Браузера#Приватность#Проверка

Эта статья входит в руководство: Что такое отпечаток браузера?

Переведите BotBrowser из исследований в продакшн

Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.