За последние полгода нам удалось победить «скликивание» нашей контекстной рекламы с бюджетом в 1 миллион рублей в месяц.
Ключом победы над фродом стал поминутный мониторинг трафика с уведомлениями об аномальных изменениях и отключением проблемных объявлений по API, и ряд отчётов, которые отражают ситуацию в реальном времени.
Рисунок 1. Диаграмма количества посетителей по ключевым словам по декаминутам
Как узнать, что вас атакуют?
Одним из первых признаков «скликивания» рекламы будет увеличение процента возвращаемых средств за фрод в Директе и AdWords.
«В Яндекс Директ расходы на фрод автоматически возвращаются на баланс рекламной кампании. Количество кликов, отсеянных системой защиты от фрода, отображается в отчетах «статистика по дням» «общая статистика» в строке «недействительные клики за весь выбранный период.»
– справка Я. Директа «недействительных кликах».
В AdWords отображение уровня «недействительных кликов» можно включить на вкладке «столбцы»:
Рисунок 2. Настроенные столбцы с уровнем «недействительных кликов» в AdWords
В нашем случае, при среднем уровне «недействительных кликов» в Директе ≈ 10%, Яндексе вдруг стал возвращать 40% рекламного бюджета, а через месяц и вовсе 54%.
Следующий признак фрода – это необоснованный рост количества переходов и сильные изменения поведенческих показателей по ряду групп объявлений.
Мы заметили, что по ряду ключевых слов, по которым никогда не было больше 200 посетителей в день, вдруг появились всплески до 3 000 посетителей. По факту, бюджет, в дни подобных активностей, мог уйти на одну рекламную кампанию, если её вовремя не останавливали.
Рисунок 3. Необоснованный рост трафика по определенной группе объявлений в Директ
Яндекс и Google не защищают от фрода
Достаточно прочесть обсуждение уровня фрода в контекстной рекламе в официальном Клубе Директа, чтобы убедиться: многие рекламодатели теряют из-за фрода деньги.
Google официально признаёт ошибки своей системы защиты и предоставляет рекламодателям «Refund Claims» (возврат израсходованного бюджета). По статистике сервиса ClickSease, Google Ads в среднем возвращает 12% рекламного бюджета.
В нашем случае, Google AdWords сначала признал 18% нашего трафика «недействительными» и вернул за них деньги, а когда мы отправили жалобу «refund claims», Google возместил ещё 13% бюджета.
Яндекс же не признаёт уязвимость собственных фильтров защиты и на жалобы об очевидных случаях фрода, отправляет шаблонный ответ, что проблема заключается в рекламодателе и его сайте.
В нашем случае, процент «недействительных кликов» в отчёте Я. Директа никогда не поднимался выше заветных 50% ни по одной из рекламных кампаний, даже в дни самых яростных всплесков, когда 80% бюджета «сливалось» на обычно мало популярную группу объявлений без звонков и заявок.
Рисунок 4. Уровень «недействительных кликов» в одной из кампаний в Директ
С каким уровнем атаки мы столкнулись
Мошеннический трафик направлялся по четырем направлениям бизнеса в двух городах. При подключении новых рекламных кампаний или запуске давно остановленных, в течение нескольких часов «скликивание» перераспределялось и на них.
Click Fraud не привязывался ко времени, и перераспределение бюджета, например на ночь, не давало никакого эффекта: свой объем левого трафика мы всё равно получали. «Скликивание» происходило одинаково активно и в РСЯ, и на поиске Яндекса, и в КМС Google.
На сайте боты симулировали поведение пользователя, переходили по разделам сайта, выделяли текст, естественно скролили и перемещали курсор.
Есть ли готовые средства защиты?
У всех сервисов защиты от кликфрода есть всего один инструмент прямой борьбы – это блокировка подозрительных IP адресов или площадок размещений в кампаниях Я.Директ и AdWords.
В случае, если против вас используются динамические IP, любой сервис борьбы с клик фродом всегда будет отставать на один шаг от мошенников в блокировке их по IP: бот уже совершит несколько кликов по вашей рекламе к тому моменту, когда сервис внесет этот IP в черный список и остановит по нему показ рекламы. К тому же, после того, как мошеннический софт не сможет увидеть вашу рекламу, он просто поменяет IP адрес, Hardware-ID и продолжит свои действия.
При атаке на рекламу в КМС или РСЯ, обычно используются различные места размещения, и системы автоматической защиты не могут обнаружить подозрительных площадок для блокировки.
Вернемся к блокировке по IP – здесь мы подходим к самому интересному – если в AdWords допускается блокировать до 500 IP адресов, то в Яндекс Директ можно блокировать всего лишь 25 уникальных IP адресов на одну рекламную кампанию! Столь маленький черный список IP адресов уже не актуален, так как сейчас можно спокойно закупить 500 IPv4 адресов за 10 тысяч рублей и обойти это ограничение.
Защититься от «скликивания», выполняемого на высоком уровне, можно всего двумя способами:
- научиться не показывать рекламу заведомо мошенническим пользователям или ботам, для чего надо найти определенные «паттерны» в их поведении и характеристиках;
- временно останавливать конкретные группы объявлений и ключевые слова, по которым идёт атака.
Отрезать часть аудитории, чтобы сохранить её большую часть
Если научиться не показывать рекламу клик ботам или мошенническим пользователям – то и навредить они не смогут.
Всегда можно отследить схожие модели поведения и паттерны, например, что фрод обычно идет по Windows 7 с 5:00 до 9:00 в Москве, и выставить корректировку ставок -100% для подобной аудитории во всех атакуемых рекламных кампаниях. Функционал корректировок ставок в AdWords достаточно обширен, чего не скажешь о корректировках в Yandex Direct.
Ищем паттерны во фроде через сервисы защиты
Для того, чтобы иметь представление о том, как именно нас атакуют, и вручную отследить закономерности в мошенническом трафике, мы подключили российский сервис защиты от фрода ClickFrog. Продукт это давно всем известный, популярный в среде CPA и так далее.
ClickFrog быстро доказал полную недееспособность:
- в день выделял не более 40 подозрительных IP адресов, при трафике с Директа в 3 000 тысячи переходов в день, и, признаваемых даже, Яндексом 1300 „левых“ кликов в день;
- основной инструмент защиты сервиса – это блокировка по IP адресу, команда о которой по API отправляется в Я.Директ, однако как только черный список в 25 IP адресов заполняется, необходимо вручную удалять последние несколько IP в каждой рекламной кампании и ждать очередного заполнения списка, и так по кругу.
Затем мы установили код американского сервиса ClickSease, нацеленного на AdWords, и пока ещё не работающего с Директом. У сервиса, кстати, есть, в отличие от ClickFrog, бесплатный тестовый период на 2 недели.
ClickSease оказался полезнее: он начал вылавливать по 300-400 уникальных мошеннических IP в день. По каждому заблокированному IP сервис отдает статистику:
- интернет-провайдер;
- площадка с которой совершился переход;
- операционная система;
- уникальный ID устройства;
- время первого и последнего перехода;
- регион.
Из отчета ClickSease нам удалось выявить закономерности во фроде:
- в 81% случаев устройство симулирует мобильную ОС: Android или iOS;
- в 59% случаев геолокация IP адреса относится не к Москве, при фроде направленном на Москву.
Ищем паттерны во фроде вручную
Однако даже таких очевидных паттернов оказалось мало для снижения вреда от фрода, да и отключать рекламу на мобильных не хотелось. Сервисы обычно способны только дать идеи по выявлению схожих паттернов во фроде, а дальше необходимо обнаружить фрод в Метрике (в случае атаки на Директ) и выделить его в отдельных сегмент Яндекс Аудиторий для последующего анализа и блокировки.
Рисунок 5. Пример анализа трафика по возрастным группам в Метрике для поиска паттернов фрода
Срезы трафика, которые помогут определить паттерны фрода:
- динамика аудитории по возрастным группам;
- динамика долгосрочных интересов пользователей;
- динамика устройств и ОС.
В случае AdWords механика противодействия ясна:
- определяем сегмент аудитории «зараженный» фродом;
- выставляем корректировку ставок -100% для выбранного сегмента;
- отслеживаем изменение показателей: конверсия, время на сайте, глубина просмотра, показатель отказов.
В Яндекс Директ механика борьбы сложнее и разделяется на два варианта:
а) вам удалось найти очевидный паттерн фрода, относящийся к полу, возрасту или мобильности:
- выставляем корректировку ставки -50% или -100% для выбранного сегмента;
- отслеживаем изменение ключевых показателей.
б) очевидных паттернов не обнаружено:
- выделяем фродовый трафик в отдельный сегмент Яндекс.Аудиторий (например, вы точно знали, что с 1 по 20 октября не могло быть 5 000 переходов по группе объявлений, по которой всегда было не больше 30 посещений в день)
- через look-alike Яндекса cоздаем сегмент похожих на наш фрод пользователей;
- выставляем корректировку ставок -100% для созданного вручную сегмента аудитории;
- аккуратно тестируем понижение ставок в рекламе для созданных Яндексом сегментов.
Строим диаграммы, которые показывают фрод
Фрод всегда порождает очевидные очаги и пики, будь то заумный софт с имитацией поведения настоящего пользователя или группа фрилансеров, выполняющих техническое задание.
Рисунок 6. Диаграмма количества посетителей по ключевым словам по декаминутам
Фрод происходит неравномерно по нескольким причинам:
- чтобы сделать атаку «сглажено» нужно владеть конфиденциальной информацией и знать кто, когда и сколько переходов совершает по вашей рекламе;
- софт действует рывками, и на минутном, 10 минутном, а иногда и на часовом графике, его действия будут бросаться в глаза;
- даже если против вас работают «школьники» с досок объявлений, то и они действуют по определенному заданию с алгоритмом, и аномалии, порожденные ими легко будет отследить.
Если научится оперативно находить и устранять очаги, то можно заметно снизить вред от фрода. В нашем случае, очевидным признаком стало аномальное увеличение количества переходов по контекстной рекламе в конкретных 10 минутах или одной минуте по некоторым ключевым словам.
Для визуализации лучше всего подойдёт Google Data Studio, поскольку корректно собирать данные, разбитые по времени на 1 и 10 минут, способен только Analytics, а Метрика, при построении отчетов по декаминутам, отдаёт некорректные показатели.
Шаг 1. Открываем редактирование полей
Шаг 2. Создаем копии следующих полей: Год, Месяц года, День месяца, Час, Минута, и называем их, например, Год (число), Месяц года (число) и так далее. Также в скопированных полях необходимо изменить Тип с формата времени и даты на „число“ как показано на рисунке.
Шаг 2. Изменяем тип скопированного поля с «даты» на «число»
Шаг 3. Создаем новое поле, в котором прописываем следующую формулу: Год (число)*10000000+Месяц года (число)*100000+День месяца (число)*1000+Час (число)*10+FLOOR(Минута (число)/10)
Шаг 3. Создаём вычисляемое поле «Время по 10 минут»
Шаг 4. Сохраняем созданное поле, затем возвращаемся к списку всех полей и находим наше новое поле «Время по 10 минут (декаминут)». Необходимо изменить его тип с «Число» на «Дата и время» как показано на рисунке, а затем обратно присвоить этому полю тип «Число».
Шаг 4. Создаём вычисляемое поле „Время по 10 минут“
Шаг 5. Создаем «Комбинированную диаграмму» и ставим как параметр наше новое поле «Время по 10 минут», как показано на рисунке. Готово.
Шаг 5. Создаём «комбинированную диаграмму»
Настраиваем уведомления на очаги фрода
Чтобы не следить за всеми случаями фрода вручную, я сделал отчёт в Google Таблицах, который обновляет данные каждую минуту и уведомляет о начале фрода.
Google Таблицы поддерживают Core Reporting API, обращаться к которому можно через «Редактор скриптов» в Таблицах.
Шаг 1. Заходим в редактор скриптов для обращения к Analytics
Рисунок 7. Редактор скриптов для обращения к Analytics Core Reporting API через Гугл Таблицы
Шаг 2. Прописываем API запрос к Analytics, чтобы получать данные о нужных показателях (например, о количестве пользователей, перешедших по платной рекламе, в каждую минуту суток, как в нашем случае).
function runDemo() {
try {
var firstProfile = getFirstProfile();
var results = getReportDataForProfile(firstProfile);
outputToSpreadsheet(results);
} catch(error) {
Browser.msgBox(error.message);
}
}
function getFirstProfile() {
var accounts = Analytics.Management.Accounts.list();
if (accounts.getItems()) {
var firstAccountId = accounts.getItems()[0].getId();
var webProperties = Analytics.Management.Webproperties.list(firstAccountId);
if (webProperties.getItems()) {
var firstWebPropertyId = webProperties.getItems()[0].getId();
var profiles = Analytics.Management.Profiles.list(firstAccountId, firstWebPropertyId);
if (profiles.getItems()) {
var firstProfile = profiles.getItems()[0];
return firstProfile;
} else {
throw new Error('No views (profiles) found.');
}
} else {
throw new Error('No webproperties found.');
}
} else {
throw new Error('No accounts found.');
}
}
function getReportDataForProfile(firstProfile) {
var profileId = firstProfile.getId();
var tableId = 'ga:' + profileId;
var startDate = "today"; //например getLastNdays(14) равняется 2 weeks (a fortnight) ago.
var endDate = "today"; //getLastNdays(0) равняется Today.
var optArgs = {
'dimensions': 'ga:date,ga:hour,ga:minute,ga:sourceMedium', // Comma separated list of dimensions.
'sort': 'ga:date,ga:hour,ga:minute', // Sort by sessions descending, then keyword.
//'segment': 'dynamic::ga:isMobile==Yes', // Process only mobile traffic.
'filters': 'ga:sourceMedium==yandex / cpc',
'start-index': '1',
'max-results': '10000' // Display the first 250 results.
};
// Make a request to the API.
var results = Analytics.Data.Ga.get(
tableId, // Table id (format ga:xxxxxx).
startDate, // Start-date (format yyyy-MM-dd).
endDate, // End-date (format yyyy-MM-dd).
'ga:users', // Comma seperated list of metrics.
optArgs);
if (results.getRows()) {
return results;
} else {
throw new Error('No views (profiles) found');
}
}
function getLastNdays(nDaysAgo) {
var today = new Date();
var before = new Date();
before.setDate(today.getDate() - nDaysAgo);
return Utilities.formatDate(before, 'GMT', 'yyyy-MM-dd');
}
function outputToSpreadsheet(results) {
var sheets = SpreadsheetApp.getActiveSpreadsheet();
var sheet = sheets.getSheetByName("coeff1");
var range = sheet.getRange('A:E');
range.clear();
// Print the headers.
var headerNames = [];
for (var i = 0, header; header = results.getColumnHeaders()[i]; ++i) {
headerNames.push(header.getName());
}
sheet.getRange(1, 1, 1, headerNames.length)
.setValues([headerNames]);
// Print the rows of data.
sheet.getRange(2, 1, results.getRows().length, headerNames.length)
.setValues(results.getRows());
}
Шаг 3. Задаем триггер на обновление данных каждую минуту:
Рисунок 8. Запрашиваем свежие данные каждую минуту для оперативного реагирования на фрод
Шаг 4. Создаем сводную таблицу из листа, обновляемого нужными данными раз в минуту, и анализируем эти показатели для настройки триггеров для уведомлений на электронную почту или же отключения групп объявлений по API Я.Директа или AdWords.
Рисунок 9. Пример настройки формул для уведомлений об аномалиях
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("notification");
var range = sheet.getRange("D2:E4");
// The row and column here are relative to the range
// getCell(1,1) in this code returns the cell at B2, B2
var cell = range.getCell(1, 2);
Logger.log(cell.getValue());
if (cell.getValue() !== "no") {
MailApp.sendEmail("your_email@yandex.ru", "Fraud notification "+cell.getValue(), "Check me "+range.getCell(1, 1).getValue());
}
else {
}
var cell2 = range.getCell(2, 2);
Logger.log(cell2.getValue());
if (cell2.getValue() !== "no") {
MailApp.sendEmail("your_email@yandex.ru", "Fraud notification "+cell2.getValue(), "Check me "+range.getCell(2, 1).getValue());
}
else {
}
var cell3 = range.getCell(3, 2);
Logger.log(cell3.getValue());
if (cell3.getValue() !== "no") {
MailApp.sendEmail("your_email@yandex.ru", "Fraud notification "+cell3.getValue(), "Check me "+range.getCell(3, 1).getValue());
}
else {
}
}
Итоги: как победить скликивание
Противодействие клик фроду можно подразделить на три группы:
а) Упреждающие действия:
- отключение «загрязненных площадок»;
- отключение показа рекламы для аудитории с фродовыми для вас признаками, например, для людей на планшетах из Санкт-Петербурга (более сложные параметры для блокировки можно использовать через списки AdWords и сегменты Метрики);
- корректировка ставок для сегментов аудитории, похожих на сегменты фрода («look-alike» сегменты создаются в Я. Аудиториях и списках Google);
- блокировка фрода по маскам IP сетей (доступно только в AdWords).
б) Профилактические действия:
- отправка жалоб о возврате бюджета в AdWords и Директ;
- расследование «кто заказал атаку на вас»;
- группировка подозрительных и часто атакуемых групп объявлений в единую рекламную кампанию;
- «ловушки» для простейших ботов, а именно скрытые кнопки на сайте, которые видны только боту и при нажатии на которые он попадает в список.
в) Действия «post factum»:
- блокировка по IP адресам;
- оперативное отключение очагов скликивания: ключевых слов, групп объявлений, рекламных кампаний, сегментов аудитории.
Рисунок 10. Способы защиты от click fraud
Полезные ссылки:
- Библиотека Core Reporting API;
- Query Explorer для удобной отработки API команд;
- Пишем скрипты для автоматизации работы с приложениями Google;
- Automate Google Sheets.
- Analytics Intelligence, чтобы спросить бота Analytics: – «А не было ли аномалий в платном трафике за последний год?» – и получить внятный ответ;
- во-первых, атакующий постарается не показывать свою рекламу по направлениям, по которым идёт атака в данную минуту, чтобы не слить свой CTR и повысить себе цену за клик;
- во-вторых, недобросовестный конкурент подберет такие ключевые слова для атаки, по которым он может перестать показывать свою рекламу без особого вреда для себя.
В нашем случае, конкурент ещё и начал клик фрод по 4 направлениям в двух городах, поэтому вычислить его не составило труда.
Чтобы было проще анализировать конкурентов, с которыми вы пересекаетесь, можно смотреть все включенные объявления конкурентов по каждому ключевому слову в интерефейсе Директа:
Рисунок 9. Все объявления конкурентов по ключевому слову
Кто также столкнулся со скликиванием контекстной рекламы – пишите в комментариях, постараемся помочь друг другу!
Автор: DanilNN