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


0. Введение 2.

Начало можно прочитать тут - http://joyreactor.cc/post/2587462
Если у вас есть какие-то вопросы по работе сайта или вам не нравится система анти-блокировщика, то можете создать отдельный пост или писать в этот http://joyreactor.cc/post/2493637 . Все, кто будут ныть тут про адблок будут забанены за тупость.


8. Ещё о моральных аспектах

Следующим логичным действием модераторов ruadlist являлось скрытие жёлтой таблички с просьбой выключить адблок. Хотя это и не являлось рекламой, но они давно скрывают не только рекламу. По поводу табличек антиадблока они даже написали полиси, где было описано что они не против табличек анти-адблока, но эти таблички должны быть максимально незаметными и закрываться по первому же чиху. О том, как они сами следуют этой полиси можно понять во-первых по предыдущим 5ти главам, где они пытались просто уйти от детектирования адблока, а во-вторых по тому, что табличку против адблока на хабре они тоже блокировали как "слишком раздражающую". Я сам эту табличку до того момента и не замечал...

Но тут они сражались "на моей территории". Я мог в любой момент изменить вёрстку как хотел. И жёлтая табличка появилась бы, а основной контент сайта исчезал. Однако это было проигрышем - пользователи не видели рекламу, не платили денег и просто ушли бы. Поэтому я такого не делал. Модераторы ruadlist могли заблокировать любую часть сайта, даже если она не относится к рекламе и просто убить сайт. Большинство пользователей посчитали бы это глюками сайта, а не проблемой адблока и ушли бы с сайта. Год назад, я пару месяцев гадал, почему в админке слетели css, пока случайно не увидел в консоли, что это адблок блокировал админский css, посчитав его похожим на брендирование.

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

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

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


9. Скрытие жёлтой таблички - уверенная победа

Хотя теперь у меня появилась дополнительная задача - не только показывать жёлтую табличку, но и не дать разломать остальной сайт - дальнейшие действия были простые.
0) Как выглядит
(далее #pageinner) на начало этой битвы:

жёлтая табличка
основной контент сайта

правая колонка
футер


1) они запрещают первый элемент внутри #pageinner, который и был жёлтой табличкой => я добавляю рандомное число дивов до и после таблички. Вёрстка выглядит так:

жёлтая табличка
#content
#sidebar


2) они запрещают внутри #pageinner всё кроме #content, #sidebar, #tagList => я ставлю дивам #content. Теперь у меня внутри #pageinner много дивов с #content. Чтобы различить их, ставлю им рандомные классы и для дива с жёлтой табличкой автогенерю css. Вёрстка получается такой:

#content .random
#content .random2 - тут жёлтая табличка
#content .random3
#content
#sidebar


3) они запрещают все #content, у которых внутри нет #contentinner => я добавляю моим жёлтым табличкам этот див


4) они запрещают див с #content и с классом. => я ставлю всем сгенеренным дивам #sidebar вместо #content и ставлю всем рандомный класс, в том числе и основному #sidebar. Вёрстка:

#sidebar .random
#sidebar .random2 - тут жёлтая табличка
#sidebar .random3
#content
#sidebar .random4
#sidebar .random5
#sidebar .random6


5) они запрещают все #sidebar, у которых не стоит #content перед ним => я делаю зебру из #sidebar и #content. Вёрстка принимает конечный вариант, который работает и по сей день (уже изменилась):

#content
#sidebar .random
#content
#sidebar .random2 - тут жёлтая табличка
#content
#sidebar .random3
#content - тут основной контент сайта
#sidebar .random4
#content
#sidebar .random5
#content
#sidebar .random6


6) они обсуждают на форуме скрыть нафиг все #sidebar - в правой колонке сайта всё равно ничего полезного нет. Я к тому времени уже придумал, как показать жёлтую табличку без классов - можно использовать css-селектор :nth-child, но они нашли что-то полезное в правой колонке и решили её не скрывать.

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

Был ещё один прикол. Модератор ruadlist решил, что чем бороться через список, проще сделать userscript, который скрывает все дивы, в которых встечаются слова "реклам" и "отключить". Так получилось, что я как раз в это время просматривал форум. И обнаружил его сообщение минут через 5 после создания. К тому времени скрипт успели установить только 2 человека (подозреваю, что он сам - один из них). Я быстро заменяю в жёлтой табличке "отключить" на "отключить", а внутри постов добавляю аттрибут data-question="хотите отключить рекламу? ;)". Его скрипт скрывает все посты и оставлят только жёлтую табличку. О чём ему пишут следующим же комментом. Он смеётся, говорит что это бессмысслено, удаляет userscript и перестаёт заниматься реактором.

Табличка - это было уже неплохо и некоторые люди задонатили денег. Но большинству было пофиг и надо было показывать им какую-нибудь рекламу. На тот момент только одна тизерка предлагала anti-adblock решение, да и то достаточно кривое и было быстро заблокированно. Мне предстояло привести их код в порядок.


a. Загрузка скриптов.

Для начала надо было загрузить скрипты с сайта тизерки. Понятно, что все их основные домены были заблочены. Они себе купили пул доменов в зоне .space за копейки. Но модераторы ruadlist не долго думая запретили загрузку любых скриптов с зоны .space на реакторе. Для этого было другое решение - можно было взять любой свой домен или поддомен и направить его на тизерку. Чтобы не передавать им реакторовские куки, мы создали отдельный домен - jr-cdn.com. На него вынесли загрузку js и css. Часть поддоменов указывают на реактор, часть - на тизерку.

Они заблокировали загрузку любых скриптов из директории /v/. Именно оттуда грузила данные тизерка, а наши скрипты находились в директории /js/. Проблема в том, что директорию тизерки я сменить не мог. Сама тизерка отвечала на вопросы и предложения крайне неохотно. А другая тизерка в ответ на наш запрос снизила нам оплату за клик в 4 раза и её пришлось вообще снять, поэтому мы не сильно пытались их теребить. 

Я мог бы сменить адрес реакторовских скриптов с /js/ на /v/, но тогда бы реактор перестал работать, а Lain_13 обычно не спешил чинить его. Тут я начал понимать, что надо думать на один шаг вперёд. Если бы я подумал, как они будут блокировать скрипты, то сразу бы понял что по имени. Если бы я сменил сам имя скрипта, то они не стали бы вносить это правило в список. Но что сделано, то сделано и надо было как-то решать эту проблему.

И, как всегда, решение нашлось. Адблок считает /v/ и //v/ - разными директориями. А большинство веб-серверов двойной слэш успешно конвертируют в одинарный. Поэтому я перенёс скрипты в /v/, назвал их так же, как и у тизерки, но обращался к ним через //v/. Они вышли из под блока и различить он их не мог.

Эта тизерка у нас крутилась на реакторе и на порнреакторе. Коды у них немного разные и потому скрипты дёргаются с разными именами. Чтобы не городить лес, я просто при загрузке страницы рандомом выбирал одно из двух имён скрипта и обращался по нему. То ли Lain_13 не заметил этого, то ли решил, что реактору скрипты не обязательны, но он внёс в список полное имя тизерного скрипта. И в половине случаев это приводило к тому, что скрипты реактора подпадали под это правило и не грузились.

Тут мне пришлось пойти по принципу "если они сломали его немного, то надо ломать полностью, чтобы было понятно, что это из-за адблока". Начали появляться жалобы, что иногда комменты открывались в новом окне - это самй явный признак незагруженных скриптов. Поэтому я добавил код, который проверяет, что основные скрипты загрузились. Если они не загружались, то весь контент сайта стирался и писали, что надо добавить в белый список домен jr-cdn.com, чтобы с него можно было загрузить скрипты. Так же я отказался от рандома. Теперь на реакторе грузились скрипты с именем их тизерки, а на порнреакторе - их.

В результате они вынуждены были убрать блокировку скриптов, так как отличить тизерные скрипты от реакторовских не могли, а если заблокировать реакторовские, то он удалялся и писал какое правило добавить в исключение адблока.


b. Показ тизеров - проба пера

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

Я понял, что чтобы показывать их, мне надо полностью переписать их хтмл-код. К счастью, они отдельно подгружали свою js-библиотеку и тизеры после загрузки запускали функцию window.VisitWeb.createBlock(), куда передавали список тизеров в виде простого массива картинка-урл-подпись. Это позволяло легко перехватить этот запрос и самому сгенерировать html. Однако по правилам рекламных площадок это категорически запрещено - нельзя изменять их код.

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

Поэтому я начал перехватывать эту функцию и генерировать хтмл-код, неотличимый от обычного поста. И вставлять его рандомно в один из постов вначале или в конце. Но оставалось два сильных отличия:
1) наши картинки лежат на поддоменах img0.joyreactor.cc/img1.joyreactor.cc c урлами /pics/post/<seotext>-12345.gif. Их картинки загружались с того же домена, что и скрипты и имели урл /i/12345.gif
2) ссылка с тизера вела на один и тот же домен ss.bevixvqe.pw

Поэтому Lain_13 легко заблокировал все ссылки на ss.bevixvqe.pw и реклама перестала показываться

И тут я обнаружил кучу проблем у адблока - они считали урлы разными, хотя с точки зрения браузера и веб-сервера они были одинаковыми:
1) http://domain.tld и http://dOmain.tld
2) http://domain.tld и http://username@domain.tld
3) http://domain.tld и http://d%6Fmain.tld

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

Список правил можете посмотреть в их меркуриал - недавно они были удалены, как нерабочие - https://hg.adblockplus.org/ruadlist/rev/0eb62780db0f


c. Показ тизеров - окончательная победа.

К тому времени я уже устал от этих головоломок и хотелось окончательно решить проблему. Тем более количество (и качество) фильтров было таковым, что под него мог попадать нормальный контент.

У меня было 3 ссылки, которые надо было замаскировать:
1) картинка. У меня они хранится по адресу http://img0.joyreactor.cc/pics/post/<seotext>-123.jpeg
2) ссылка с картинки. У меня они идут на адрес http://img0.joyreactor.cc/pics/post/full/<setotext>-123.jpeg
3) ссылка с текста. У меня они идут на адрес http://link.joyreactor.cc/redirect?url=....

Тут я объясню всё на примере первого пункта. Остальные делались аналогично.

Я осознал, что у адблокеров нет никакой поддержки регэкспов. А на моём фронтэнде стоит nginx, который с регэкспами хорошо работает. Поэтому я начал вместо обычных ссылок на картинки вида http://adserver.joyreactor.cc/i/<image_id>.jpeg ставить такой урл:
http://img0.joyreactor.cc/pics/post/<image_id>-<random_text>-<image_id>-12345.jpeg

На nginx была поставлена проверка на регэксп и если начало и конец были одинаковыми, то он загружал картинку с тизерного сервера. Если не одинаковый текст, то грузил обычную картинку. Image_id у тизерного сервера был достаточно большим, поэтому вероятность случайного срабатывания этого правила была крайне мала. Кому интересно, вот кофиг nginx:

  set $vw_first 0;
  set $vw_second 1;
  if ($uri ~* "^/pics/post/([0-9a-f]{48})-[^/]*-([0-9a-f]{48})-(\d+)\.([^\.]*)$"){
  set $vw_first $1;
  set $vw_second $2;
  }

  if ($vw_first = $vw_second) {
  rewrite "-(\d+)\.([^\.]*)$" /visitweb/$vw_first.$2 last;
  }

Это позволило сделать картинки и урлы неотличимыми от обычных картинок и урлов с точки зрения адблока. Теперь эти тизеры постоянно показывались пользователям адблока.


d. Показ тизеров - ложка дёгтя

Хотя пользователи адблока смотрели тизеры и иногда кликали на них, тизерке (вероятнее всего) не понравились эти пользователи и они постепенно снизили доходность клика в 5 раз. С этих тизеров шли копейки и оставляли мы их больше для того, чтобы они постоянным пользователям мозолили глаза сильнее, чем обычная реклама.

Так же есть другой блокировщик рекламы - Adguard. Он позволяет вставлять javascript на страницу сайта. И они вставили код:
Object.defineProperty(window, 'VisitWeb', { get: function() { return document.createElement('div'); } });
Таким образом они так же, как и я, перехватывают window.VisitWeb.createBlock(), но ничего не показывают. И не дают это никак перезаписать - блокируя таким образом их показ.


e. Текущее положение

Основная проблема - найти рекламные сети, которые готовы работать с адблокерским трафиком. Большинство - очень неповоротливы и медленны.

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

В Актуальных проблемах RuAdList наши домены стоят на первом месте, что не может не радовать. По сути модераторы листа признались в своём бессилии. (Через несколько часов после выпуска поста они удалили сайт оттуда =) ).

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

Есть и другие мелкие листы и скрипты, которые скрывают рекламу. Их всех объединяет одно свойство - они малоиспользуемые. Когда я увижу какой-нибудь популярный скрипт, то я сразу легко блокирую его действие.

Очень показательная история: Фейсбук заявил, что теперь будет показывать рекламу в том числе для тех, у кого стоит блокировщик рекламы. Через 2 дня вышла новость от адблока - https://geektimes.ru/post/279420/ , где они смеялись и издевались, что легко обошли эту рекламу "всего" за 2 дня. Сейчас я попытался узнать, что с этой войной - похоже она уже подчистую проиграна Адблоком. В реддите пишут, что реклама показывается - https://www.reddit.com/r/Adblock/comments/506kqc/will_adblock_remove_facebook_sponsored_publication/ и вроде единственный способ её обойти - это ставить userscript, который навряд ли поставит большое количество пользователей.

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


f. Будущее

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

Я вижу несколько способов блокировщикам рекламы противостоять этому:
1) закрытые списки - они даются только определённым людям и ставятся различные "вотермарки", чтобы узнать от кого прошла утечка. Достаточно сложно в реализации. Если станет популярным, то утечки будут случаться часто.
2) обфусицировать списки, чтобы не так легко можно было понять что именно блокируется. Это такой же вопрос толщина брони vs сила снаряда. И у владельцев сайтов будет сильное преймущество в финансировании - для них деобфусикация даст кучу денег.
3) не давать клиентам списки. В этом случае весь трафик клиента должен проходить через внешнюю проксю блокировщика рекламы. Не уверен, что многие пользователи захотят пропускать трафик от своего интернет-банка или Paypal через чужие сервера.
4) сделать много разных списков. Сейчас надо посмотреть штук 5 листов, чтобы охватить 95% пользователей блокировщиков рекламы. Если этих списков будет сотня, то владельцам сайтов будет сложнее бороться с ними. Но возникнет проблема совместимости. Я и сейчас вижу по форумам проблемы связанные с тем, что один пользователь включает у себя сразу 2-3 списка из текущих 5.
5) полная блокировка сайта, показывающего рекламу. Если сайт достаточно большой, то пользователи не будут использовать такие фильтры.

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