Недетектируемый браузер: что считывает детектор
Какие поверхности считывает страница проверки, почему вердикты двух поставщиков расходятся и какую запись результатов по каждому детектору вести вместо обещания невидимости.
BotBrowser Team
Нужна структурированная документация по теме Отпечатки?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
«Недетектируемый браузер» это утверждение о вердикте, а вердикт принадлежит тому, кто запустил проверку. Страница проверки не спрашивает, честен ли браузер: она считывает набор поверхностей, сравнивает их с моделью, которой владеет, и возвращает оценку, пороги и веса которой задаёт поставщик. Два детектора могут считать разные поверхности одной сессии и прийти к противоположным выводам, при этом ни один из них не обязан быть сломан. BotBrowser предоставляет настраиваемую среду выполнения Chromium и документирует, какие сигналы умеет держать согласованными, но не гарантирует вердикт третьей стороны и не публикует долю прохождений, потому что такая доля описывает связку версии детектора, цели и сессии, а не свойство браузера. Ниже перечислены поверхности, доступные детектору, причины расхождения показаний и поля записи, которая заменяет обещание.
TL;DR
navigator.webdriverэто одно логическое значение. MDN фиксирует, что Chrome выставляет его при--enable-automation,--headlessили--remote-debugging-port=0, поэтому флаг сообщает, как браузер был запущен, а не то, доверится ли страница результату.performance.now()намеренно огрублён ради защиты от атак по времени и отпечатков: MDN указывает разрешение 5us в документе с cross-origin isolation и 100us в остальных случаях. Проба тайминга, выполненная при одном разрешении, несравнима с той же пробой при другом.- Вердикт является функцией собственной модели детектора. Два поставщика, считывающие разные поверхности, могут разойтись в оценке одной сессии, и каждый при этом может быть прав относительно того, что измерил.
- Утверждение заменяется записью: имя и версия детектора, считанные поверхности, применённая граница прохождения, конфигурация сессии и отметка времени. Запись можно сравнить, утверждение нельзя.
- Практический результат это таблица результатов по каждому детектору с названными режимами отказа, а не прилагательное в названии продукта.
Содержание
- Что может считать страница проверки
- Почему два детектора расходятся в оценке одной сессии
- Заменить утверждение записью измерения
- Практический вывод
Что может считать страница проверки
Детектор читает то, что может прочитать любой скрипт на странице. Это и есть вся поверхность, и её стоит перечислить, потому что список конечен и не выглядит впечатляюще.
Флаги навигации и автоматизации
navigator.webdriver это член этой группы, который страница видит первым, и сам по себе он несёт один бит. MDN определяет его как свойство только для чтения, которое указывает, управляется ли браузер автоматизацией, и перечисляет конкретные условия Chrome, при которых значение истинно: флаг --enable-automation, флаг --headless или --remote-debugging-port с портом 0. Каждое из этих условий описывает конфигурацию запуска. Страница, читающая логическое значение, узнаёт, как был запущен процесс, а страница, читающая только этот флаг, решила доверять одному биту.
Соседние сигналы описывают тот же запуск с других сторон. Присутствие привязки, специфичной для автоматизации, на объекте window, порт отладки, отвечающий на локальном интерфейсе, строка идентификации с названием сборки без графического интерфейса и объект window.chrome, форма которого отличается от штатной установки, указывают на один вопрос: запущен ли этот браузер так, как запускается браузер человека. Ни один из них не отвечает, будет ли сессия признана доверенной, потому что детектор вправе присвоить им нулевой вес.
Строка идентификации и Client Hints дают разные срезы одного заявления. navigator.userAgent возвращает одну строку, а navigator.userAgentData отдаёт массив brands из пар { brand, version }, поле mobile и строку platform. Метод getHighEntropyValues() принимает список полей, среди которых architecture, bitness, model, platformVersion и uaFullVersion, и возвращает Promise с запрошенными значениями. Детектор, читающий только brands, видит меньше, чем детектор, запросивший uaFullVersion и bitness, и это ещё один пример разного набора поверхностей.
| Сигнал | Что описывает | Почему слаб сам по себе |
|---|---|---|
navigator.webdriver | Применялись ли флаги автоматизации | Одно логическое значение, и поставщики могут его игнорировать |
Привязка автоматизации на window | Какая библиотека внедрила скрипты | Отсутствует при прямом запуске, присутствует в некоторых фреймворках |
| Доступный порт отладки | Открыта ли точка доступа протокола | Закрытый порт обычен для браузера человека |
| Строка user agent | Чем браузер себя объявляет | Настраивается и остаётся заявлением, а не измерением |
Форма window.chrome | Семейство сборки Chromium | Зависит от сборки, а не от степени доверия |
Хранилище и разрешения
Хранилище это четвёртая группа, и она меняется вместе с историей пользователя, а не со сборкой браузера. Доступность cookie, партиционирование стороннего хранилища, повторяемость ответа при проверке квоты и факт предыдущего посещения источника описывают прошлое сессии, а не её конфигурацию. Детектор, читающий эти признаки, спрашивает, бывал ли этот профиль здесь раньше, а это другой вопрос по сравнению с тем, автоматизирован ли браузер.
Квота и занятость хранилища доступны через navigator.storage.estimate(), который возвращает Promise с полями quota и usage в байтах. Перечисление баз через indexedDB.databases() возвращает имена и версии, а caches.keys() перечисляет имена кэшей. Три вызова дают детектору сведения о накопленном состоянии без чтения содержимого: сам факт наличия базы с именем или кэша с версией уже отличает новый профиль от использованного.
Разрешения ведут себя так же. Набор разрешений, которые документ может запросить, состояние, возвращаемое каждым из них, и то, вызывает ли запрос системное окно, наблюдаемы без какого-либо взаимодействия с пользователем. Значение Notification.permission и результат navigator.permissions.query({ name: 'geolocation' }) дают два примера таких чтений. Страница поэтому может построить картину прежних решений профиля о согласии, и новый профиль с долгоживущим отвечают по-разному даже при одинаковой сборке браузера.
Именно поэтому задача мониторинга или сравнения должна осознанно решить, нужен ей свежий профиль или постоянный, и записать выбор. На этой поверхности оба дают разные ответы, и ни один не заслуживает большего доверия: это просто разные сессии.
Тайминг и производительность
Тайминг легко упустить из виду, потому что он выглядит измерением машины, а не браузера. MDN фиксирует, что performance.now() огрублён намеренно: разрешение 5us в документе с cross-origin isolation и 100us в остальных случаях. Это двадцатикратная разница в наименьшем наблюдаемом интервале, и она является свойством состояния изоляции документа, а не оборудования.
Последствие для сравнения прямое. Проба, измеряющая наименьшую ненулевую разницу между двумя вызовами performance.now(), читает разрешение, а не машину. Та же проба в изолированном и неизолированном документе даст числа, различающиеся в двадцать раз. Детектор, считывающий поведение таймера, наблюдает состояние изоляции документа, в котором выполняется, а команда, меняющая это состояние между запусками, изменила измерение.
Свойство crossOriginIsolated доступно странице как логическое значение и позволяет детектору сразу отнести документ к одной из двух групп разрешения, не измеряя интервалы вовсе. Запись сессии без этого поля оставляет неопределённым, какое из двух значений мог наблюдать таймер.
Второй таймер ведёт себя иначе. Date.now() возвращает целое число миллисекунд, поэтому проба, построенная на нём, не различает интервал меньше одной миллисекунды и не показывает огрубление performance.now(). Обратный вызов requestAnimationFrame приходит с частотой обновления экрана: примерно шестьдесят раз в секунду при 60 Гц и примерно сто двадцать раз в секунду на панели 120 Гц. Детектор, смешивающий три источника времени, получает три разных предела точности и ещё одну причину записать конфигурацию документа.
Сеть и медиа
RTCPeerConnection раскрывает странице, создавшей соединение, информацию о локальных кандидатах, поэтому сбор кандидатов это один из способов проверить утечку WebRTC. Тот же вызов показывает, поддерживает ли путь соединения протокол вообще, и страница может отличить заблокированный путь от рабочего, ни разу не завершив вызов. Медиаустройства, отрисовка canvas, параметры WebGL, метрики шрифтов и обработка звука относятся к тому же виду поверхностей: каждая является легитимным API, чей вывод зависит от окружения, и каждую можно считать, не запрашивая ничего необычного.
Список медиаустройств через navigator.mediaDevices.enumerateDevices() зависит от разрешения: MDN указывает, что устройства кроме устройств по умолчанию попадают в список только после выдачи разрешения, поэтому документ, который его не запрашивал, видит устройства по умолчанию и не видит меток. Поле kind при этом остаётся заполненным, поэтому детектор может считать классы устройств без запроса; deviceId, label и groupId возвращаются пустыми строками, пока захват не разрешён. Отрисовка на canvas зависит от драйвера, сглаживания и шрифтов системы. Параметры WebGLRenderingContext.getParameter() называют производителя и модель средства визуализации, AudioContext отдаёт sampleRate, а DynamicsCompressorNode раскрывает ratio, knee, attack и release. Чтения canvas, WebGL и audio не требуют вызова getUserMedia; заполненные идентификаторы и метки устройств требуют разрешения на медиа или захвата.
Сетевые метрики добавляют ещё один независимый срез. navigator.connection.effectiveType принимает значения slow-2g, 2g, 3g или 4g, поля rtt и downlink дают оценку задержки и пропускной способности, а saveData сообщает о режиме экономии трафика. Записи performance.getEntriesByType('resource') содержат nextHopProtocol, transferSize, encodedBodySize и decodedBodySize, поэтому страница различает протокол соединения и объём переданного тела без доступа к содержимому ответа.
Реестр детекторов BotD показывает, какие проверки действительно выполняет один детектор с открытым исходным кодом, а что на самом деле проверяют публичные наборы детекторов сравнивает наборы поверхностей нескольких публичных наборов. Смысл перечисления вместе в том, что эти поверхности независимы. Детектор, считывающий только тайминг, только canvas или только флаги навигации, смотрит на другой срез той же сессии, а изменение браузера, удовлетворяющее одному срезу, может сдвинуть другой.
Почему два детектора расходятся в оценке одной сессии
Расхождение структурно и не является ошибкой ни одного из продуктов.
У каждого поставщика своя модель
Вывод детектора это оценка, произведённая весами и границами прохождения, которые поставщик выбирает и пересматривает. Два поставщика могут считать одну сессию и прийти к разным вердиктам по трём обычным причинам: они считывают разные наборы поверхностей, придают общей поверхности разный вес и размещают границу прохождения в разных точках. Ни одна из этих причин не видна со стороны браузера.
Именно поэтому доля прохождений не является свойством браузера. Она является свойством браузера вместе с версией детектора, целью и сессией. Одно число без трёх остальных это измерение с удалёнными условиями.
| Причина расхождения | Что различается | Что может сделать браузер |
|---|---|---|
| Разные наборы поверхностей | Один поставщик читает тайминг, другой canvas | Ничего напрямую: набор выбирает поставщик |
| Разные веса | Один и тот же сигнал значит больше или меньше | Ничего: веса внутренние |
| Разные границы прохождения | Граница находится в другой точке | Ничего: граница не публикуется |
| Другая версия детектора | Модель изменилась между выпусками | Повторить проверку после каждого выпуска |
| Другая цель | Другой сайт использует другую конфигурацию | Проверять фактическую цель |
Один браузер, два вердикта
Сессия, которая проходит у одного поставщика и не проходит у другого, является обычным случаем, а не противоречием, которым кажется. Проходящий поставщик может не считывать изменившуюся поверхность, а непроходящий может считывать только её. Команда, читающая пару как «браузер наполовину недетектируем», изобрела метрику, которую не публикует ни один поставщик.
Полезное прочтение уже. Каждый вердикт это утверждение о модели одного детектора, применённой к одной сессии. Два вердикта вместе говорят, что сессия находится у границы в одной модели и с запасом внутри другой, а это информация о границе, а не о браузере.
Чего не может означать слово «недетектируемый»
Слово обещает результат, который не может дать никакое локальное изменение, потому что результат производится в другом месте моделью, которой оператор не управляет. Конфигурация может убрать конкретный флаг, согласовать конкретный набор сигналов и держать профиль постоянным на протяжении запуска. Она не может решить, что сделает с результатом модель поставщика, и не может обещать, что модель останется прежней в следующем месяце.
Отношение к слову как к заявлению о продукте, а не как к описанию прошлого наблюдения, это ошибка, к которой располагает эта категория. Поставщик, публикующий долю прохождений, опубликовал одно измерение в условиях, которые могут не совпадать с вашими.
Заменить утверждение записью измерения
Альтернатива утверждению это запись, которую можно перепроверить. В ней пять частей, и каждая недорога.
Пять полей
Первое это детектор: его имя и версия или дата модели, вынесшей вердикт. Без версии результат нельзя воспроизвести после обновления поставщика. Второе это набор поверхностей, который детектор указал, его собственными словами, потому что названия категорий поставщика остаются единственным общим словарём. Третье это граница прохождения или категория, в которую попал вердикт, а не пересчитанный процент. Четвёртое это конфигурация сессии: сборка браузера, идентификатор профиля, флаги запуска, маршрут, локаль, часовой пояс и состояние изоляции документа. Пятое это отметка времени и цель.
| Поле | Пример формы | Почему его нужно записывать |
|---|---|---|
| Личность детектора | имя поставщика и дата модели | Смена модели обесценивает сравнение |
| Указанные поверхности | категории в терминах поставщика | Эти метки остаются единственным общим словарём |
| Категория вердикта | прохождение, проверка, блокировка или термин поставщика | Пересчитанный процент теряет границу прохождения |
| Конфигурация сессии | сборка, профиль, флаги, маршрут, локаль, изоляция | Две конфигурации это не одно измерение |
| Цель и время | класс URL и отметка времени | Один поставщик оценивает разные цели по-разному |
Фиксация конфигурации, породившей вердикт
Поле конфигурации легко пропустить, и именно оно делает запись полезной. Идентификатор сборки браузера, идентификатор профиля, действующие флаги запуска, регион выхода маршрута, согласованная локаль и состояние cross-origin isolation документа вместе описывают сессию достаточно подробно, чтобы второй запуск можно было настроить так же. Записывать состояние изоляции важно по причине из раздела о тайминге: оно меняет разрешение, доступное пробе таймера, в двадцать раз.
Записывайте действующие значения, а не запрошенные. Запрошенная локаль, которую сайт переопределил, или запрошенный маршрут, который переключился на резервный, дают сессию, не совпадающую с записью, если запись не фиксирует то, что применилось на самом деле.
Таблица результатов вместо прилагательного
Результат, выдерживающий проверку, это таблица с одной строкой на детектор и одним столбцом на каждое поле выше, плюс столбец наблюдаемого вердикта и столбец режима отказа, если детектор заблокировал. Читатель видит, что браузер прошёл два детектора и был остановлен проверкой у третьего, и может решить, важен ли третий для его цели.
Такая таблица является честным ответом на вопрос, который подразумевает название продукта. Она сообщает, что измерено, кем, при какой конфигурации и когда. В ней нет слова «недетектируемый», потому что это слово стёрло бы каждый её столбец.
Что перепроверять после изменения
Три изменения обесценивают сохранённый результат. Выпуск детектора меняет модель, поэтому строку нужно измерить заново, а не перечитать. Обновление браузера или профиля меняет конфигурацию сессии, поэтому поле конфигурации в строке перестаёт совпадать. Изменение цели, включая новую версию проверки на том же сайте, меняет то, что проверялось.
Всё остальное можно использовать повторно. В этом практическая выгода записи: она превращает открытый вопрос в небольшой набор строк, которые нужно обновить при конкретном изменении.
FAQ
Делает ли браузер недетектируемым установка navigator.webdriver в false?
Нет. MDN описывает это свойство как сигнал о том, применялись ли флаги автоматизации, и детектор вправе присвоить ему нулевой вес или сверить его с другими наблюдениями. Страница, читающая boolean, прочитала один бит, а модель поставщика читает многие, и её веса не публикуются.
Почему один и тот же браузер проходит один детектор и не проходит другой?
Потому что два поставщика считывают разные поверхности, придают общим поверхностям разный вес и ставят свои пороги в разных точках. Сессия у границы в одной модели может находиться с запасом внутри другой. Пара вердиктов описывает две модели, а не две половины одного браузера.
Можно ли публиковать долю прохождений как свойство продукта?
Долю можно публиковать только с её условиями: детектор и версия модели, цель, конфигурация сессии и окно измерения. Без них число описывает измерение, которое невозможно воспроизвести, а последующее обновление поставщика обесценит его без какого-либо изменения со стороны браузера.
Практический вывод
Начинайте со списка поверхностей, а не с прилагательного. Прочитайте, что может считать страница проверки, зафиксируйте, что navigator.webdriver сообщает о флаге запуска, а разрешение performance.now() зависит от состояния изоляции документа, и примите, что вердикт производит модель, которой оператор не управляет.
Следующее действие это одна таблица. Выберите детектор, важный для вашей цели, запишите его имя и дату модели, поверхности, которые он указал, категорию, которую он вернул, и полную конфигурацию сессии вместе с состоянием изоляции, затем запустите проверку дважды при одинаковой конфигурации, чтобы увидеть, устойчива ли строка. Две совпадающие строки доказывают повторяемость измерения, а не вердикт, и это различие составляет суть.
Sources
Эта статья входит в руководство: Что такое отпечаток браузера?
Похожие статьи
BotD: какие детекторы действительно выполняются
ОтпечаткиЧто на самом деле проверяют публичные наборы тестов
ОтпечаткиДоказательства и журналы Cloudflare Bot Score
ОтпечаткиОтчёт Intl CreepJS: поля и отсутствующие значения
ОтпечаткиФингерпринтинг Network Information API: проверка политики
ОтпечаткиФингерпринтинг частоты кадров: экран и видео
Выполните ту же проверку в BotBrowser
Загрузите профиль, сохраните внутри браузера те сигналы, о которых только что прочитали, и подтвердите результат на своей машине до раскатки.