Немного пятничный пост. В последнее время заметил что есть реклама которая пробивает сразу 2(!) расширения блокирующие её. И обычно это очень низкокачественная и навязчивая реклама. Решил разобраться как так, и, возможно, даже попробовать побороть эту дрянь. Кому интересно — прошу под кат (осторожно, много картинок).
А чем мешает то?
Я — ярый сторонник блокировки рекламы. Особенно — низкокачественной. Если я не хочу смотреть рекламу на определённом сайте — то такая реклама это деньги на ветер для рекламодателя (бесполезный показ), неуважение ко мне, и нулевой профит вообще для кого либо. Оговорюсь сразу — есть сайты на которых я сознательно вырубаю адблоки (тот же хабр, например). Это сайты которые выбирают тематическую рекламу, а не «АЙФОНЫ СО СКЛАДА 70% СКИДКА!».
И тут на днях на одном дружественном мне сайте (новостной портал в моём городе) появляется ну ооочень низкопробная реклама в стиле «электрики долго скрывали — чтобы экономить электричество надо всего лишь...». Сначала я подумал что их взломали, но потом оказалось что админ этого сайта просто ввязался в какую-то партнёрку которая работает поверх адблока. И мало того что такая реклама это просто стыд, так она ещё и ломала местами вёрстку. Закрыть её стало делом принципа.
Попытка 1 — в тупую, или...
… «пфф, да ща напишу кастомный фильтр для адблока». Пишем фильтр, сохраняем, тестим… А страница то ломается, прям совсем. И косметические фильтры (блокировка по css) тоже не работают. Как так? Но, видимо, поэтому и не было такого фильтра «из коробки». Как окажется позже — наш «зловред» хитрый и заинлайнен, а хромообразные браузеры не позволяют расширениям блокировать инлайновые скрипты. Ну и ну…
Попытка 2 — меняем адблок
Слово «адблок» уже давно стало нарицательным, как джип или ксерокс. Так что на самом деле у меня стоит не adblock а ublock (надеюсь это не сочтут за рекламу?). Поэтому для начала тестим работу разных блокировщиков из топа выдачи, которые обещали скрываться от хитрозадых скриптов, и блокировать даже их. Выхлопа, на самом деле, это не дало, и часть рекламы продолжала пролезать (хотя и не вся, но всё же).
Попытка 3 — смотрим врагу в глаза
И нет, я не имею ввиду админа ресурса :) Приступим к самому интересному. Лезем смотреть исходники страницы, в надежде найти нашу гадость. И… Бинго!
Огромная обфусцированная кодина, которая явно тут не спроста. Смотрим похожие сайты (в плане рекламы) — да, это явно оно. И обфусцировано добротно, не банальным p.a.c.k.e.r-ом.
Немного распакуем, чтобы понять где зарыта собака. Если присмотреться на монстра, можно заметить что это самовызывающаяся функция, внутри которой есть три основные части — большая функция, какой-то массив с числами и небольшой обработкой его же, и вызов первой функции. Скопируем массив и его обработку, запускаем, смотрим вывод.
Переменная «а» у нас хранит весь набор слов которые нельзя было заменить на однобуквенные. А в первой части мы видим много фрагментов вида а[42], a[69], и т.д. Обфускация оказалась не такой уж и сложной. Делаем замену которая не гарантирует нам работающий код, но сильно облегчит чтение.
Уже видно более-менее читаемый код. Пропускаем через beautifier, и находим желаемый участок кода (я удалил неважный код чтобы поместиться в одном скрине).
Эта зараза делает запрос на определённый юрл, и в случае отвала (попадания в фильтр адблока, например) — вызывает window.stop(), чем ломает всю страницу. Довольно хитро, и встроенным функционалом адблока это, вроде, не решаемо. А заблокировать весь скрипт не позволяет хром, ибо инлайн.
Что же делать?
После некоторых раздумий, у меня получилось расширение для браузера (ещё одно, да-да) которое занимается чисто блокировкой этой рекламы. Пока что оно заточено именно на эту сеть, потому как других я не знаю. Если кто захочет — форкайте, кидайте пулл реквесты, или отпишитесь в комментах — добавлю поле для добавления своих адресов.
Работает оно следующим образом — перехватываем запрос на «проверочный» адрес, с помощью
Расширение заливать никуда не стал, ибо надо платный аккаунт, это proof-of-concept, сегодня пятница, и ещё 1000 и 1 малозначимая отмазка. Так что код на гитхаб.
UPD. В комментариях возникли вопросы, поэтому тезисно о главном:
Почему не блокировать все инлайн скрипты — потому что это сломает логику многих сайтов, не подходит
Почему не возвращать 200 (обманывать) — потому что хром так не умеет. Либо 500ую, либо пропускаем
Почему не переопределять window.stop сходу — потому что надо успеть во временной отрезок когда DOM-блок head уже существует, но аякс ещё не вернулся. Это ограничение extensions API.
Комментарии (56)
Aingis
20.10.2017 12:49Довольно хитро, и встроенным функционалом адблока это, вроде, не решаемо. А заблокировать весь скрипт не позволяет хром, ибо инлайн.
Вообще-то uBlock умеет отключать инлайн-скрипты (надо включить галочку «Я опытный пользователь»):
Или можно добавить фильтр CSP, который тоже будет блокировать инлайн-скрипты или даже хитро решать какие запускать с помощью параметраnonce
, но для этого уже надо отдельное расширение.vlreshet Автор
20.10.2017 12:55А если не надо вырубать вообще все инлайн скрипты, а только этот? Лично у меня не получилось составить такой фильтр. Если умеете — научите :)
vlreshet Автор
20.10.2017 13:05И в догоночку — сайт который меня и смутил — CMS на DLE. И там дофига логики в виде инлайн js. Поэтому блокировать всё через CSP — не выход
VereVa
20.10.2017 15:38body > script:nth-child(N)
, где N это его порядковый номер
но это не спасет, если они сменят порядок скриптов
PbIXTOP
20.10.2017 12:49Смотря на исходники увидел URL сети — не проще его было заблокировать?
Блокировка есть ведь разная бывает. при желании можно и 200 отдать.
Посмотрел свои списки — этот домен оказался в рабочих proxy-anonimazer списках.vlreshet Автор
20.10.2017 12:50Извиняюсь, но вы читали пост? Если
не проще его было заблокировать?
если скрипт видит что этот юрл заблокирован — он ломает всю страницу. Собственно в этом и вся проблема
vlreshet Автор
20.10.2017 13:02А, а про «200» отдать — может я что-то упустил в документации, но вроде нельзя возвращать произвольные хедеры в ответ скрипту. Или блокируем, или нет, третей опции не дано. Плюс если оно увидит что есть адблок, и при этом получило 200ую — начнёт совать рекламу, с чем мы, собственно, и боремся.
datacompboy
20.10.2017 14:40/etc/hosts
127.0.0.1 evil.network
/etc/nginx/sites-enabled/evil.network
location ~ / {
return 200 'bye';
}vlreshet Автор
20.10.2017 14:44С таким успехом можно сам сайт к себе перенаправить, вырезать из html ненужные скрипты, и отдавать обратно :)
datacompboy
20.10.2017 14:50разница большая — между «делать что-то с контентом» и «зараутить мусор в девнул».
у меня много таких сетей как раз зароучено на 127.0.0.1.
Те, кто не мешают жить при этом — просто довольствуются 404м. те, кому надо чтоб сервер спел и сплясал — получают 200тку.vlreshet Автор
20.10.2017 14:59Ну это нормальное решение для девелопера, но не очень для рядового пользователя. Кстати, я не совсем понял из кода этой заразы, так как он специально запутан, но по-моему если оно получает 200, но респонс ему не подходит — то оно считает это провалом.
esc
21.10.2017 00:08Не считает. Но поправить это не проблема. Тогда вместо /dev/null прийдется делать эмулятор. Для каждого сайта.
crower
20.10.2017 19:05Пришёл к такому-же решению, когда не смог при помощи uBlock победить одну раздражающую текстовую рекламу. Раздражает, потому что красным цветом выбивается из сине-голубой гаммы сайта и шрифты заголовка рекламного блока значительно крупнее информативного сайта. uBlock, кстати, блок прячет, но только при первой блокировке, а потом эта зараза снова вылазит. Наверно потому что в скрипте рефрешер периодически обновляет спрятанный блок. А индентификторы блока на каждой странице свои.
Апач всё-равно крутится на компе, страницей/сайтом пользуюсь с разных компов. Так почему бы ходить не на оригинальный сайт, а на локальную страничку, в которой вырезана вся дрянь?mihmig
21.10.2017 00:20как-то давно баловался с proxomitron, а как Вы предлагаете вырезать из страниц рекламу?
crower
21.10.2017 06:31Сейчас у меня этим занимается скрипт на perl, поэтому реализовать можно любой достаточно сложный алгоритм поиска и удаления. Вот сегодня научил его подгружать новую версию скрипта, включенного в страницу, когда разработчики сайта, внеся изменения, меняют и его имя (типа с script.013.js на script.014.js).
algotrader2013
21.10.2017 10:22Какое-то время оно работать будет. Но чувствую, что следующим шагом упыри начнут не просто ждать 200 а ждать подписи переданного хеша секретным ключом сервера.
nmaltsev
21.10.2017 13:03chrome.webRequest.onBeforeRequest.addListener(function(e){ // some conditions return {redirectUrl: 'data:text/html;charset=utf-8,<html style="background:#000;"><script>window.close();</script></html>'}; });
Есть еще возможность сделать redirect на страницы закодированные в data url (html страницу, картинку или скрипт)esc
21.10.2017 13:49А с чего вы взяли, что ответ будет интерпретироваться исполняться как html со скриптами?
nmaltsev
21.10.2017 19:02Если редиректить страницу, открывающуюся в новой вкладке, или в iframe, то будет выведена вся закодированная страница со всеми скриптами.
П. С. решение проверено на практике
Dmitry_7
20.10.2017 13:25Причина-то в администраторе сайта. Почему, используя методы социальной инженерии, не принудить отказаться?
Например, запустить в этой сети баннер 'наш админ иванов и.и. — ....'?esc
21.10.2017 00:09Кому вы предлагаете это запускать? Владельцу сайта, который сознательно установил подобный код, чтобы не лишать сайт дохода от пользователей с блокировщиками.
inoyakaigor
20.10.2017 13:38Во, может тут кто-нибудь мне подскажет как заблокировать Яндекс.директ который порой пролезает сквозь блокер? Например на тех же сайтах Яндекса
vlreshet Автор
20.10.2017 14:22А дайте пример такого сайта, ради интереса)
juray
20.10.2017 15:29у меня есть смутные воспоминания где-то полугодовой давности о борьбе с рекламой в почтовом интерфейсе то ли яндекса, то ли майлру.
Суть была в том, что при попытке косметически заблочить рекламный блок, также блочился и список папок почты, что разумеется, неприемлемо. Я тогда плюнул, и решил потерпеть.
Сейчас вот проверил — в яндексовой почте сетевой рекламы не вижу даже при отключенном адблокере (иногда всплывает внизу панель с рекомендацией мобильного приложения).
А вот на мэйле реклама пролезает, и в коде страницы творится что-то, на мой взгляд довольно безнадежное — все идентификаторы обфусцированы примерно так:
<div id="MkgdI63" class="MkgdI5P MkgdI26"></div>
причём, при каждом заходе на страницу эти строки меняются. То есть косметические фильтры ublock тут не пригодны.vlreshet Автор
20.10.2017 15:32Кстати да, с этим беда. Я на страницу mail.ru себе stylish делал, чтобы был только бокс с количеством новых писем, и черный фон (без рекламы, новостей, и.т.д.). Пару дней назад стиль сломался, снова всё вылезло. Ну ёпт…
причём, при каждом заходе на страницу эти строки меняются.
А меняются вооообще-воообще все названия, или есть статические? Тогда к ним можно было бы привязаться, и потом :child(n) делатьesc
21.10.2017 00:11А что, проблема генерировать случайное к-во пустых div перед нужным, чтобы :child(n) не работало?
juray
21.10.2017 13:59есть статические — относящиеся к шапке и подвалу. Всё что в середине — выглядит вот так.
Уточнение: всё это веселье — если смотреть инспектором, или через интерфейс «заблокировать элемент» в uBlock, то есть в уже сформированном DOM. В исходном коде страницы во встречающихся фрагментах html идентификаторы не обфусцированы. Только попробуй, догадайся, какой див к какому элементу относится.
esc
21.10.2017 00:10www.liveinternet.ru/stat/ua например. Адблок под Хромоподобными браузерами не справляется. uBlock- да.
inoyakaigor
21.10.2017 01:50Я.Музыка например
crower
21.10.2017 08:56На Я.Музыке ghostery сказал, что заблокировал 6 элементов, среди которых и Yandex.Direct.
В правом сайдбаре остались одна картинка-рекомендация, ведущая на какие-то тизеры.
Там, кстати, исходного html практически нет — всё на скриптах, так что победить можно только редактируя уже сформированную страницу, правя на лету скрипты и перехватывая запросы.
qw1
21.10.2017 10:54А дайте пример такого сайта, ради интереса)
Тут — https://habrahabr.ru/post/338580/#comment_10434492
Xandrmoro
21.10.2017 12:55drive2.ru юблок вообще отказывается грузить, если не добавить яндекс в исключения, например.
sotnikdv
21.10.2017 08:24У меня кончилось блоком всего от Яндекса. На DNS. И mail.ru тоже, глобально, все, что относилось к mail.ru. Слишком тесно сплели функционал и рекламу, которая для меня очень навязчива. Пришлось отказаться от функционала.
qw1
21.10.2017 10:58См. мой коммент чуть выше. Там пример, когда блока сетей Яндекса недостаточно, потому что сайт-партнёр устанавливает прокси на своём домене, и через этот прокси загружаются как все баннеры, как и остальной контент сайта.
eugenebb
20.10.2017 16:52Было бы интересно сделать возможность менять ID для рекламы на чьё-нибудь, кого не против поддержать (можно сделать через webRequest.onBeforeRequest)
Т.е. ходишь по интернету и большинство просмотренной рекламы идёт на добрые дела.
caudatecoder
20.10.2017 19:06+3Встречал похожий пример борьбы с adblock'ом, только не на самой рекламе, а на модальнике с просьбой отключить adblock.
Выпиливание модальника из DOM или скрывание с помощью css приводило к перезагрузке страницы. Аналогично нашел обфусцированный inline script, который с интервалом в 2 секунды проверял целостность модальника.
Вот такой хак со stackoverflow для остановки всех интервалов позволил убрать назойливый модальник.
Может и вам в вашем расширении однажды пригодиться :)sotnikdv
21.10.2017 08:27Жму плечами и вкидываю сайт (как ни странно, на действительно нужных мне серьезных сайтах таким не страдают) в blacklist. При попытке захода на него предлагает или поискать нормальный источник или посмотреть котяток.
Пока ни одного случая, когда в блеклист попадало что-то нужное, ибо страдают таким только откровенные помойки, из того что я сталкивался.
Так что режем хвост по самую голову. У нас тут serious business, нам не до шуток ;)
esc
21.10.2017 00:01ublock этот код обходит (со своими стандартными списками). Там сейчас появилось что-то вроде встроенного user script. Но очень топорным способом сейчас это делается.
adblock вообще очень инертен в этой борьбе. Там только вебсокет нормально блокировать научились в конце августа. Видимо, неудачи с монетизацией сильно подкосили их энтузиазм.
esc
21.10.2017 00:23var startTime = new Date().getTime(); var randNumber; while ((new Date().getTime() - startTime) < 5000){ randNumber = Math.random(); // because an error can happen if this block is empty, or needless }
Без обид, но вы бы еще майнер встроили…vlreshet Автор
21.10.2017 09:49Я знаю что код странный, но на то есть причины. While с пустым телом как-то нестабильно себя ведёт, очень нестабильно в плане времени. Пробовал делать просто какой-то мусор в теле — видимо компилятор палит то что он не используется далее, оптимизирует, и в итоге получается снова пустое тело. И только вот так получилось заставить нормально держать ровно 5 секунд ?\_(?)_/?
esc
21.10.2017 09:55Код не то. чтобы странный, он 5 секунд выедает 100% ядра в бесконечном цикле. После изменения кода на такой, что будет делать 3-4 попытки, пользователю прийдется 15-20 сек ждать пока ваш рандомайзер открутится. Не говоря уже о том, что могут пошутить и бесконечное число попыток сделать, при другом сценарии это мешать не будет, а вот с вашим решением в доме появится лишняя печка.
vlreshet Автор
21.10.2017 12:58На самом деле это одна из причин почему я не допиливал это расширения до продакшн-вида, чтобы можно было залить его в маркет, и т.д. Потому что хром не умеет по-человечески задерживать запрос, а мой костыль — ну очень уж не оптимальный. Но, как proof-of-concept — почему бы и нет?
esc
21.10.2017 13:51Не мучайте браузеры тех, кто решится ваше расширение поставить. Вот пример для этого прокси из ruadlist js fixes:
// piguiqproxy.com circumvention prevention scriptLander( function() { let _open = XMLHttpRequest.prototype.open; let blacklist = /[/.@](piguiqproxy\.com|rcdn\.pro)[:/]/i; XMLHttpRequest.prototype.open = function(method, url) { if (method === 'GET' && blacklist.test(url)) { this.send = () => null; this.setRequestHeader = () => null; console.log('Blocked request: ', url); return; } return _open.apply(this, arguments); }; } );
vlreshet Автор
21.10.2017 18:32Я выложил не готовое расширение, а код на github. Человек который знает как его установить себе и запустить — наверняка прочитает исходники, и не будет мучать свой браузер не зная того) Да и опять таки — я нигде не говорил что у меня шикарное решение которое подойдёт всем и каждому.
P.S. ruadlist js затестил, работает, блокирует. Спасибо за совет)
Ivan_83
21.10.2017 04:53Раньше пользовался проксимитроном, он ловко кромсал страницы на лету.
Вроде привокси умеет тоже самое.
Сейчас влом этим заниматься из за https.
Ещё где то в инете был плагин или ещё что то для запрета исполнения обфусцированных скриптов :)
vlreshet
Рядовой пользователь обречён страдать и/или платить, иного не дано.
Не важно про что речь: ИТ, сантехника, электрика, авто, медицина…
Или ты разбираешься сам или отстёгивай специалистам за свои хотелки.
Londoner
21.10.2017 11:24По-моему, это тот случай, когда не зазорно ещё и автоматически прокликивать эту рекламу в бэкграунде. Вот — vc.ru/21708-adnauseam
esc
21.10.2017 13:53Открою секрет, такая реклама в основном по cpa работает и «скликать» ее можно разве что сделав фейковый заказ.
Tallefer
21.10.2017 14:16Просьба проверить ту рекламу под RU AdList JS Fixes и RU AdList CSS Fixes,
если это возможно (не уверен, что в хроме все будет работать).
И зарепортить разрабу, пусть включит в новый выпуск.
Все же лучше объединять усилия. :)
vesper-bot
Думаю, проще было бы переопределить window.stop() сразу, сохранив старую функцию в другом объекте, и как минимум логать к ней доступ по stackTrace или как оно в джаваскрипте устроено. Можно даже парсить его, определяя, насколько глубоко сидит вызвавший кролик, и насколько вызов стопа в этом месте валиден, и пропускать внутрь, если да. По идее, должно сломать всех любителей останавливать все скрипты в окне.
vlreshet Автор
А как? Chrome extension не может напрямую модифицировать windows объект (в документации так написано, да и тестил). Поэтому нужно только вставлять в тело html инлайн скрипт который это сделает. А вставить его надо до того как запустится наш зловред. А пока документ не создал блок head — нам некуда инлайнить. А если head уже отрисовали — то уже поздно, так как зловред в нём. Как-то так.
esc
В этом коде window.stop не используется. Хотя есть ребята, что сделали по образу и подобию через window.stop, но по факту способов убить страницу на столько много, что надоест переопределять.
Есть способ лучше, переопределить XHR, как сделали в user script на форуме список для адблока. Конечно, это обходится, но, как минимум, код на сайтах нужно будет менять.