Как я блокировщик рекламы блокировал. Часть 1.

0. Введение.

В данной статье я хочу рассказать о том, как боролся с блокировщиками рекламы (далее адблок). Как и все посты в админских историях, он рассчитан на технически подкованную аудиторию. Так же не хотелось бы слушать нытьё "Ах вы фошисты! У меня деды воевали против рекламы! У меня брат от попандера умер!". Для этого уже существует другой пост - http://joyreactor.cc/post/2493637


1. Моральное обоснование

Моральный вопрос борьбы с адблоком для меня хорошо сформулировал amarao в комменте https://geektimes.ru/post/271638/comments/#comment_9049910 :

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

Я считаю возможным делать всё что угодно, пока это остаётся в рамках технической сферы. Я бы и ЦП разрешил постить, но моралфаги наинают использовать средства вне технической сферы - полиция изымает сервера и владельцев.

Многие придерживаются таких же ценностей технофашизма даже не осознавая это. Никто не жалуется на производителей фильмов, что они используют более сильное шифрование. Жалуются, когда после взлома этого шифрования и проигрыша в технической сфере, они переходят в сферу политическую и начинают вводить всякие драконовские законы и требования. Так же я ни разу не слышал, чтобы жаловались на последнюю систему защиты игр (Denuvo), которую уже полгода не могут взломать. Понятно, что если они сделали такую систему, значит они умнее и лучше. Не согласен - садись сам за дизассемблер и ломай.


2. Экономическое обоснование борьбы

Вообще, я считаю более правильным переход на систему платных подписок. Более правильной - потому что она пассивная. Не требует от меня дополнительных телодвижений. Один раз реализовал и она работает. В отличие от этого, анти адблок нужно смотреть - не написали ли они ещё какой-то фильтр. Но для этой системы надо быть реально большим, крутым и нужным. Не уверен, что реактор сможет это сделать оставшись самим собой. Текущий прогрессбар надоначенного справа очень красноречиво показывает это.

Однажды порнотизерка, которая стоит на порнреакторе, предложили протестировать их анти-адблок код. Мы попробывали и с удивлением обнаружили увеличение дохода в 2 раза. Оплата там по кликам. То есть все те адблокеры, которые кричат "я не хочу смотреть рекламу! я никогда на неё не кликаю!" тоже замечательно её смотрели и кликали. Количество показов выросло немного более, чем в 2 раза - всё же процент кликающих уменьшился, но очень немного.

А увеличение доходов в 2 раза - это очень круто. Это бы решило все проблемы из-за падения курса. Поэтому я занялся разработкой анти адблока.


3. Детектирование адблока - первые победы

Первое, что надо сделать - это понять, есть ли адблок у человека. Для этого (да и вообще для будущего) скачиваем самый популярный список правил в рунете - ruadlist - https://easylist-downloads.adblockplus.org/ruadlist+easylist.txt . Есть и другие, о них я ещё напишу.

Для начала пошёл простым путём: создаём <div id="adv"></div>, добавляем его вниз страницы и провряем, виден ли он. Если адблок не установлен, то будет виден. Если установлен, то он будет блокироваться и не виден. Но дальше начинается интересное - если адблок установлен, но сайт добавлен в белый список, то див всё равно виден не будет! И лишь через некоторе время он появляется. То есть, адблок вначале всё блокирует, а позже смотрит а не в белом ли списке сайт - может блокировать и не надо? Решается оно просто - добавлением таймера и проверкой через некоторе время, но осадочек остался.

Реакция модераторов списка ruadlist была достаточно быстрой и простой - они разрешили div#adv на сайтах реактора. Таким образом они попытались показать, что адблока у человека не стоит. Думаю с моралфажеской точки зрения, пытаться наебать меня с тем, есть у человека адблок или нет - неправильно. И именно поэтому моралфаги идут нафиг. С позиции технофашизма всё понятно и логично. Поэтому и вновь продолжается бой.

Я достаточно оперативно отслеживал их изменения в списке. И зная, что обновление листа у пользователя по-умолчанию происходит раз в 4 дня не спешил с ответными обновлениями. Если я обновлю свой детектор через 2 дня, то половина пользователей даже не узнает, что они вносили изменения.

Дальше изменения с моей стороны тоже понятные - я меняю id=adv на какой-нибудь другой заблокированный (их там сотни). Через 3 таких итерации (я меняю, они вносят этот id в белый список для реактора), они сдаются и ставят правило:

@@||joyreactor.cc^$generichide

(здесь и далее, правило возможно не точное, мне вломы искать какое было именно оно, но идею я передаю). По этому правилу на joyreactor.cc не срабатывают никакие правила скрытия. Но реклама всё равно не показывается - они просто блокируют запросы на получение рекламы. И определить адблок тоже не получается - все дивы видны.


4. Детектирование адблока - патовая ситуация

Переходим к следующему уровню - надо понять, не блокируются ли запросы. Тут проблема в том, что из-за cross-domain policy это сделать не легко. Загрузить скрипты через ajax запрещает это полиси, а при загрузке просто через <script> нельзя узнать что загрузилось и загрузилось ли что-то. И тут мне помогает ctrmanager.

Ctrmanager.com - это наш сайт. С помощью него ставится и снимается реклама. Если реклама напрямую от рекламодателей, то там же и хранятся картинки. Если от других сетей, то оттуда идёт js-код, подгружающий рекламу с их серверов. Функционал вроде простой, но когда мы искали сторонние решения для наших объёмов, то цена в несколько раз превышала доходы с этой рекламы.

На ctrmanager при выдаче рекламы я добавил дополнительный хидер "Access-Control-Allow-Origin: *". Это позволило делать обычный ajax-запрос на этот домен. И соответственно получать результат и ошибки. Модераторы ruadlist спокойно добавили ctramanger в белый список. Этим они меня озадачили. С одной стороны, теперь можно спокойно показывать прямую рекламу. Но с другой стороны, никакой прямой рекламы у нас не было из-за кризиса и не предвиделось. Единственное что было - это вшивая тизерка внизу страницы. Сейчас лишь иногда появляется что-то другое. Соснули все - адблокерам будет показываться прямая реклама, но мы денег не получили так как её нет. Это, конечно, хорошо когда все соснули - но как-то неинтересно.


5. Детектирование адблока - позиционная война

Экспериментируя с запросами я обнаружил странную работу с crossdomain ajax в фаерфоксе. Там получилось различить из-за чего заблокирован запрос - из-за адблока, или из-за crossdomain policy. Я залил этот детект понимая, что лучше что-то, чем ничего. На их форум посыпались жалобы на жёлтую табличку, но они у себя ничего не видели. Видели безобидный ajax-запрос http://yandex.ru/favicon.ico?ads-bla-bla, но он блокировался браузером из-за полиси. Как потом оказалось, это срабатывало только на английской версии ФФ (возможно в каких-то ещё случаях). Продолжалось это несколько дней в течении которых я смеялся и злорадствовал, а они не могли понять в чём проблема. Кстати так же я добавлял в белый список user-agent основного модератора списка - для него детект просто не запускался. Уже точно не помню, но вроде он полдня недоумевал, почему к нему приходят жалобы на реактор, хотя у него всё хорошо.

Эти моменты добавили веселья, но ход войны был не в мою пользу. Детект работал плохо. Надо было придумывать что-то радикальное с загрузкой рекламы. И оно придумалось.


6. Детектирование адблока - пробитие бреши в обороне.

У элемента <script> есть атрибут onerror, который вызовется если скрипт не получилось загрузить. Отлично! Но для теста загрузить рекламный скрипт не получится - мы же не можем его остановить. Он сразу начнёт подтягивать остальную рекламу. Но после недолгих поисков обнаружился скрипт на сайте рекламодателя, который ничего не делал, однако блокировался общими правилами. Поэтому в код детекта добавлено:

var script = document.createElement('script');
script.onerror = function(e) {
  show_anti_ab();
};
script.src = "http://bad.ads/test.js";
document.head.appendChild(script);

Понятно, что на это модераторы добавили этот скрипт в белый список для моего домена. И у меня было 2 дня на то, чтобы найти новый такой пустой скрипт. Так как блокировалось там куча всего, то найти была не проблема (и у меня был один на примете), но я понимал что следующим правилом они внесут в белый список все домены (как generichide - есть genericblock) и для реактора просто отдельно заблокируют только рекламные запросы. Поэтому надо было двигаться дальше.


7. Детектирование адблока - победа.

Код рекламодателей выглядит обычно так:

var el = document.createElement('script');
el.src = "http://get.my.ads/ads.js?ua=" + user_agent + "&w=" + width + ....;
document.getElementsByTagName("head")[0].appendChild(el);

Было бы замечательно в этот код добавить el.onerror=.., но мало того, что изменение кода рекламодателя категорически запрещено, но ещё и загруженный скрипт часто тоже выглядит так же и подгружает какой-нибудь скрипт дальше по цепочке. И тут мы вспоминаем, что в javascript есть возможность перезаписывать любые функции. Поэтому добавляем такой код к детекту:

var _createElement = document.createElement;
document.createElement = function(nodeName){
   var el = _createElement.call(document, nodeName);
   if(nodeName.toLowerCase() == 'script'){
     el.onerror = function(){on_js_load_error(el)};
   }
   return el;
};

Тут просто автоматом добавляется onerror ко всем созданным скриптам. Теоретически их могут перезаписать, но рекламодатели обычно этим на заморачиваются. В функцию on_js_load_error передаётся элемент и получив у него атрибут src можно узнать какой именно скрипт не загрузился. Если это url нашего рекламодателя, значит или рекламодатель умер, или у пользователя адблок включён. 99.99% времени сайт рекламодателя работает.

Это была полная победа. Они не могли скрыть элементы на странице - я бы это обнаружил. Они не могли запретить запросы - я это тоже обнаружу. Но если они разрешат и показ всех элментов и запросов, то реклама будет показываться. Поэтому от попыток скрытия адблока они отказались. Все, что они до этого добавляли в белые списки, чтобы скрыть адблок, было убрано. Мой детект работал успешно.

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

Продолжение следует...