Страница не может спросить, стоит ли у вас блокировщик рекламы — она проверяет исход: приманку, заблокированный запрос или отсутствующую переменную скрипта.
Не существует браузерного API, который отвечал бы на вопрос «установлен ли блокировщик рекламы?». Страница не может запросить это напрямую, как запрашивает ваш часовой пояс или размер экрана. Поэтому сайты, которым нужен ответ, не спрашивают — они расставляют небольшую ловушку и смотрят, что с ней случится. Если ловушка уцелела, ничего не заблокировано. Если нет — что-то вмешалось.
Ключевые выводы
- Страница делает вывод о блокировщике рекламы по результату, а не по прямому сигналу. Три распространённые проверки — это элемент-приманка, на скрытие которого рассчитаны списки фильтров, сетевой запрос по рекламному адресу, который не проходит, и глобальная переменная, которую рекламный скрипт должен был определить, но так и не успел.
- Одни и те же проверки работают независимо от того, классическое ли это расширение или декларативный движок правил. Расширения, работающие через скрипты, и декларативные движки вроде WebKit Content Blockers на iOS или
declarativeNetRequestв Chrome применяют одни и те же шаблоны из списков фильтров, поэтому оставляют на странице одинаковый наблюдаемый след. - Сама по себе детекция возвращает один бит — «что-то вмешалось», — но то, какие именно приманки сработали, может сузить круг сильнее. Разные списки фильтров нацелены на разные имена классов и шаблоны URL, поэтому то, какая приманка уцелела, а какая нет, само по себе уже небольшой сигнал для отпечатка.
- Сетевые проверки могут сработать ложно и без установленного блокировщика. DNS-фильтрация, встроенная защита от отслеживания в браузере или корпоративный прокси дают точно такой же результат «реклама не загрузилась» — хотя скрыть элемент-приманку ни один из них не способен.
- Это статья про детекцию, а не про обход. Здесь нет инструкций, как скрыть блокировщик от сайта или обойти стену анти-адблока — это отдельная, состязательная тема, которую мы здесь не рассматриваем.
Три проверки, на которых строится почти любая детекция блокировщика
Уберите все вариации, и почти любая проверка на блокировщик рекламы на вебе сводится к одному из трёх тестов, которые запускаются вскоре после загрузки страницы.
Элемент-приманка. Страница создаёт <div> (или похожий элемент) с именем класса и структурой, в точности похожими на настоящее рекламное место — что-то вроде ad-banner, pub_300x250, text-ad, той же ширины и в той же позиции, что и настоящий рекламный контейнер. Страница вставляет его и ждёт мгновение. Блокировщики рекламы работают в основном по спискам фильтров (EasyList и его аналоги), которые находят элементы по этим же самым селекторам, поэтому настоящий блокировщик скроет или удалит приманку вместе с любой настоящей рекламой, подходящей под тот же шаблон. Затем страница проверяет, на месте ли приманка: не стал ли display равен none, не обнулился ли offsetHeight, не был ли узел полностью удалён из DOM. Любой из этих признаков означает, что что-то вмешалось. Проверка плагинов и расширений от BrowserInsight использует именно этот тест: она вставляет за пределы экрана div с классами вроде pub_300x250, text-ad и ad-banner, ждёт около 100 миллисекунд, а затем считывает его вычисленный стиль и размеры.
Обратите внимание: это чисто косметический тест. Приманку создаёт локально собственный скрипт страницы, сетевого запроса здесь нет вовсе — заставить её исчезнуть может только то, что применяет правила скрытия элементов внутри самой страницы.
Заблокированный сетевой запрос. Вместо того чтобы следить за элементом, страница отправляет запрос по адресу, похожему на рекламный или трекинговый вызов — с путём, содержащим /ads/, именем хоста, совпадающим с известным рекламным доменом, или скриптом с именем, похожим на известный аналитический тег. Блокировщик с правилами сетевой фильтрации (а не только косметического скрытия) останавливает запрос до того, как он покинет браузер, либо браузер сообщает об ошибке ответа. Собственный скрипт страницы видит этот сбой — отклонённый fetch, изображение, у которого сработал onerror вместо onload — и воспринимает это как сигнал.
Отсутствующая глобальная переменная. Настоящие рекламные скрипты после успешной загрузки обычно определяют что-то в глобальной области видимости — функцию, объект конфигурации, флаг, который проверяет собственный код издателя перед тем, как продолжить. Если сам рекламный скрипт был заблокирован и не загрузился, эта переменная никогда не будет определена. Страница проверяет её наличие в тот момент, когда тег скрипта должен был бы выполниться; undefined означает, что скрипт так и не запустился.
У всех трёх подходов одна и та же форма: задать ожидание, которое выполняется только если ничто не вмешалось, а затем проверить, выполнилось ли оно. Ни один из них не задаёт браузеру прямой вопрос, потому что задать его попросту негде.
Почему это работает одинаково для любого типа блокировщика
Логично было бы предположить, что расширение и встроенный блокировщик без единого расширения выглядят для детектора совершенно по-разному. На практике это не так, и причина в том, что оба реализуют одни и те же правила из списков фильтров разными механизмами.
Обычное расширение-блокировщик внедряет content scripts и стили, которые скрывают или удаляют подходящие элементы, а также может напрямую перехватывать сетевые запросы. Декларативный движок фильтрации устроен изнутри иначе, но приходит к тому же результату: браузер заранее получает скомпилированный список правил и применяет его сам, без того чтобы код блокировщика просматривал каждую страницу. Именно так работают WebKit Content Blockers, впервые появившиеся в Safari и лежащие в основе любого приложения-блокировщика на iOS: приложение передаёт список правил в формате JSON, и дальше WebKit применяет его на уровне движка к каждой загружаемой странице. Такие правила умеют и блокировать ресурс, и скрывать элементы по CSS-селектору (действие css-display-none), поэтому срабатывают и сетевые проверки, и проверка-приманка. Платформа расширений Chrome движется в похожем направлении: declarativeNetRequest позволяет расширению передать браузеру статический или динамический набор правил вместо того, чтобы проверять каждый запрос на JavaScript, и в Manifest V3 именно этой модели Chrome ждёт от расширений, блокирующих сетевые запросы. (Скрытие элементов в блокировщике на MV3 по-прежнему делается через внедрённый CSS, но списки фильтров за ним те же.)
Механизм разный — скомпилированный список правил, применяемый движком, против скрипта, следящего за страницей, — но результат, который может наблюдать страница, одинаков в обоих случаях: элемент-приманка, подходящий под правило фильтра, скрывается или удаляется, а запрос, подходящий под правило фильтра, не загружается. Детектору, построенному вокруг результата, а не механизма, не нужно знать или заботиться о том, с каким именно типом блокировщика он имеет дело. Именно поэтому мобильный блокировщик рекламы, реализованный «всего лишь» как блокировщик контента для Safari, который не может запускать скрипты или читать содержимое страницы, всё равно срабатывает на ту же самую проверку элемента-приманки, что и десктопное расширение.
Что это выдаёт
Сам по себе положительный результат такой проверки — это почти один бит: да, что-то фильтрует эту страницу, или нет. Этот единственный бит уже полезен сайту — именно он запускает стену анти-адблока или баннер «пожалуйста, добавьте нас в белый список» — но сам по себе это не такой уж сильный сигнал для отпечатка, потому что слишком многие посетители дают одинаковый ответ.
По-настоящему точным его делает запуск сразу нескольких приманок, каждая из которых оформлена под соглашения своего списка фильтров. Детектор, который подставляет пять приманок-div одновременно — одну под общий шаблон EasyList, одну под соглашения регионального списка, одну под правило, которое есть только в более строгом списке против «раздражающих элементов» — и проверяет каждую по отдельности, получает не просто вывод «заблокировано: да». Он узнаёт, какие именно шаблоны сработали, а сочетания подписок на списки фильтров у разных пользователей отличаются достаточно, чтобы эта конкретная комбинация сужала круг людей, среди которых вы теряетесь. Это та же математика энтропии, что лежит в основе любого сигнала отпечатка: редкое сочетание результатов идентифицирует сильнее, чем распространённое — именно об этом наш материал про энтропию отпечатка и множество анонимности, только применительно к сигналам в целом. Детекция блокировщика рекламы — это небольшой пример той же идеи с низкой энтропией: стоит нескольких битов, но не является самостоятельным идентификатором.
Стоит чётко обозначить, чем эта техника не является. Это не то же самое, что определение, какие именно расширения у вас установлены — та техника работает через проверку собственных доступных ресурсов расширения или его характерных побочных эффектов на странице, и она гораздо более прицельная, нацеленная на конкретное расширение. Детекции блокировщика рекламы не нужно знать, какой именно продукт отвечает за это — ей достаточно знать, что что-то вмешалось в расставленную приманку.
Ложные срабатывания: когда проверка срабатывает без единого установленного блокировщика
Поскольку проверка следит за результатом, а не задаёт прямой вопрос, её запускает всё, что даёт тот же результат, — а несколько совершенно обычных настроек, не связанных с расширениями, делают именно это. Важно, однако, какую именно проверку они задевают: всё, что работает на уровне сети, может вызвать срабатывание проверок заблокированного запроса и отсутствующей переменной, но не может тронуть приманку, которую страница создала локально.
- Блокировка рекламы на уровне сети. Pi-hole в домашней сети, фильтрующий DNS-резолвер или блокировщик на уровне роутера не пропускают рекламные запросы к рекламному серверу — на устройстве нет ни одного расширения, но рекламный скрипт не загружается и его глобальная переменная не появляется, в точности как при блокировке расширением.
- Встроенная защита от отслеживания в браузере. Enhanced Tracking Protection в Firefox по умолчанию блокирует известный трекинговый контент в приватных окнах, а в строгом режиме — во всех окнах, без каких-либо дополнений. Если «реклама», которую загружает детектор, лежит на домене из её трекерных списков, этот запрос тоже не пройдёт.
- Браузеры со встроенным блокировщиком рекламы. Brave Shields, а также встроенные блокировщики в Opera, Vivaldi и других браузерах сами применяют списки фильтров. В зависимости от настроек это могут быть и правила скрытия элементов — так что они способны задеть даже проверку-приманку без единого установленного расширения.
- Корпоративный или школьный прокси. Многие управляемые сети пропускают трафик через фильтрующий прокси, который попутно вырезает рекламные и трекинговые домены как побочный эффект более широкой политики контента, никак не связанной с тем, что установил сам пользователь.
- DNS-резолверы, разоблачающие замаскированные трекеры. Некоторые фильтрующие DNS-сервисы идут дальше и отслеживают CNAME-записи, чтобы распознать трекеры, скрытые через CNAME cloaking, — сторонний трекер за поддоменом, похожим на first-party. В результате рекламный или трекинговый вызов может быть заблокирован, даже если его URL выглядит как безобидный собственный адрес сайта.
Ни в одном из этих случаев нет установленного расширения, и всё же каждый из них может заставить страницу решить, что «блокировщик рекламы обнаружен». Сайт, который считает этот сигнал однозначным доказательством установленного расширения, делает вывод, которого данные не подтверждают: он знает лишь, что где-то между страницей и рекламным сервером что-то вмешалось.
Чего детекция не сообщает сайту
Стоит прямо обозначить границу, потому что легко переоценить, что доказывает прохождение или провал проверки-приманки. Страница, запускающая этот тест, не узнаёт, какое именно расширение у вас установлено, если оно вообще есть — для этого нужны более прицельные техники прощупывания расширений из нашего гайда по приватности расширений. Она не узнаёт вашу личность или историю посещений. И, как показывает список ложных срабатываний выше, по одному лишь неудавшемуся запросу она не может отличить расширение от сетевой фильтрации, строгих настроек приватности браузера или прокси в управляемой сети. Максимум, что она узнаёт — это небольшое число битов о том, какие наблюдаемые шаблоны были нарушены при этой конкретной загрузке страницы.
Эта статья о том, как строится такой вывод и что он может и не может подтвердить — а не руководство по обходу детекции. Постоянная борьба между авторами списков фильтров и поставщиками анти-адблока — отдельная тема, выходящая за рамки этой статьи; ничего из написанного выше не является рецептом, как скрыть блокировщик от сайта или проскользнуть мимо стены анти-адблока.
Посмотрите, что выдаёт ваша собственная настройка
Проверка плагинов и расширений от BrowserInsight запускает тест с приманкой параллельно с проверками расширений, так что вы можете сами увидеть, что видит сайт, когда проверяет ваш браузер этим способом. Поскольку этот тест косметический, чисто сетевой блокировщик вроде Pi-hole в нём не проявится. Если результат вас удивил — «заблокировано» без единого расширения или «не заблокировано», хотя расширение стоит, — проверьте, нет ли в браузере встроенного блокировщика и не отключено ли в расширении скрытие элементов или не добавлен ли сайт в белый список. Чтобы увидеть, как такая проверка вписывается в полную картину отпечатка браузера, загляните в проверку отпечатка — там показано остальное, что страница может узнать о вашей конфигурации.
Часто задаваемые вопросы
Может ли сайт точно узнать, каким именно блокировщиком рекламы я пользуюсь?
Нет, не с помощью проверок по результату вроде элемента-приманки или неудавшегося запроса. Такие тесты лишь устанавливают, что что-то вмешалось в контент или запросы, похожие на рекламу — они не могут определить конкретное расширение или механизм блокировки, ответственный за это. Чтобы назвать конкретное расширение, нужна другая, более прицельная техника, нацеленная на его собственный след.
На мобильных устройствах это работает так же, как на десктопе?
Да, для декларативных блокировщиков. Приложение-блокировщик контента на iOS, построенное на WebKit Content Blocker API, даёт тот же наблюдаемый результат, что и десктопное расширение — скрытый элемент-приманка, неудавшийся рекламный запрос — даже не имея возможности выполнять скрипты на странице. Детектору не нужно знать, какой именно механизм отвечает за это.
Может ли у меня сработать детекция блокировщика, если я его не устанавливал?
Да. Фильтрация на уровне сети (домашний DNS-блокировщик, корпоративный прокси) и встроенная в браузер защита от отслеживания могут вызвать срабатывание проверки заблокированного запроса или отсутствующей переменной, а браузер со встроенным блокировщиком рекламы, например Brave, может задеть и проверку-приманку — всё это без единого установленного расширения.
Насколько велик риск приватности от обнаружения блокировщика рекламы?
Сам по себе это небольшой сигнал — ближе к одному биту, чем к отпечатку. Он становится более идентифицирующим только тогда, когда сайт одновременно запускает несколько по-разному оформленных приманок и фиксирует, какие именно из них сработали, — потому что этот конкретный шаблон совпадений отличается между пользователями сильнее, чем простой ответ «да/нет».


