Атака на аб-тест: рецепт ‘r’+t(101)+’es46’

Пара месяцев назад сервис рекомендаций REES46 начал предлагать сравнение собственной совокупности рекомендаций с сервисом Retail Rocket через АБ-тесты в формате «пари» с обязательством заплатить 100 000 рублей при проигрыша.

Первые тесты пройдены и в отечественную редакцию поступило изучение аналитиков Retail Rocket (копия имеется в распоряжении издания) в котором авторы столкнулись с достаточно необычными результатами. Предлагаем вам для ознакомления полную версию изучения совершённого АБ-теста.

АБ-тестирование совокупностей рекомендаций в веб-магазине «ДочкиСыночки»

В веб-магазине «ДочкиСыночки» в течение нескольких месяцев шел тест трех рекомендательных совокупностей: Retail Rocket, REES46 и внутренней совокупности компании.

Механика проведения АБ-тестирования: вся аудитория сайта случайным образом делится на три равных части, и любая часть аудитории видит собственную версию сайта. Изменяются лишь блоки персональных рекомендаций — каждому сегменту показываются блоки, управляемые одной из рекомендательных совокупностей:

В рамках теста измеряется конверсия каждого сегмента трафика, сравнивается с другими и по итогам принимается ответ о том, какая совокупность трудится действеннее.

Аудитория делится на клиенте посредством JavaScript-кода, все пользователи приобретают идентификатор одного из трех сегментов теста, что сохраняется в куке и после этого передается в Google Analytics при каждом значимом действии на сайте.

Результаты теста на момент написания статьи из Гугл Analytics — конверсия по сегментам

Сегмент А — рекомендательная совокупность Дочки Сыночки

Сегмент В — рекомендательная совокупность REES46

Сегмент С — рекомендательная совокупность Retail Rocket

Трансформации конверсии относительно показателей внутренней рекомендательной совокупности «ДочкиСыночки»

Согласно этой информации сегмент С (Retail Rocket) проигрывает, сегмент B (REES46) побеждает. Раздельно обратите внимание 27 мая, в данный сутки Retail Rocket показывает лучшие показатели — к данной подробности мы возвратимся позднее.

В течение теста инженерная команда Retail Rocket совершила множество внутренних тестов, распознала пара неточностей на сайте, исправила много неприятностей с интеграцией и совершила комплект внутренних тестов разных их вариаций и алгоритмов. Ощутимых трансформаций все эти действия не принесли.

Визуальная оценка качества рекомендаций

У нас в Retail Rocket имеется пара качества оценки рекомендаций и способов эффективности. Самый первый из них – так называемая “экспертная оценка” (субъективная визуальная оценка “адекватности”).

взглянуть на примеры рекомендаций, организованные совокупностями Retail Rocket и REES46:

Атака на аб-тест: рецепт 'r'+t(101)+'es46'

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

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

Косвенная оценка качества рекомендаций

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

Прежде всего мы решили изучить аудиторию, которая взаимодействует с блоками товарных рекомендаций. При клике товары в рекомендательных блоках REES46, к URL добавляется параметр:

Мы добавили похожий параметр в URL товаров из блоков рекомендаций Retail Rocket:

И выстроили в GA сегменты кликавших в рекомендательные блоки пользователей:

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

В случае если это так, то отечественные блоки должны приобретать меньше кликов, чем блоки рекомендаций REES46, что опровергается данными Гугл Analytics — мы приобретаем в 2,81 раза больше кликов по виджетам:

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

В этом случае кликнувшие в блоки рекомендаций Retail Rocket будут конвертироваться хуже, чем кликнувшие в блоки REES46. Но согласно данным Гугл Analytics это не верно, конверсия кликнувших в блоки Retail Rocket существенно выше (на 37% согласно данным за 4 дня):

Так, Retail Rocket существенно чаще рекомендует релевантные пользователю товары, пользователи чаще кликают на рекомендации и эти товары положительно воздействуют на продажи.

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

Изучение аудитории вебмагазина

Начав изучить данный сегмент аудитории, мы увидели два интересных факта:

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


Дабы проверить корректность работы разделения трафика вебмагазина на сегменты, мы самостоятельно протестировали сегментатор посредством кода, что применял сайт: параллельно с главным делением, запустили сегментирование той же аудитории — погрешность оказалась минимальной:

  • Сегмент 1: 63215 пользователей
  • Сегмент 2: 63500 пользователей
  • Сегмент 3: 63686 пользователей

Это указывает, что сегментатор трудится верно и погрешности в пара процентов быть неимеетвозможности, т.е. распределение трафика в рамках АБ-теста «ДочкиСыночки» содержит аномалию.

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

Логичным предположением стала идея, что пользователи каким-то образом смогут перемещаться между сегментами. В отечественной практике виделись случаи, в то время, когда пользователи поменяли сегмент в теста, к примеру из-за неправильно заданного времени судьбе куки (в одном из магазинов кука, в которую сохраняли идентификатор сегмента АБ-теста, жила лишь 14 дней, и в случае если пользователь возвращался по окончании этого времени, ему присваивалось случайно значение — т.е. пользователь имел возможность попасть в второй сегмент теста). Дабы избежать аналогичных обстановок, у нас создан чек-лист, в котором имеется пункт о необходимости убедиться, что пользователь не меняет сегмент на протяжении теста.

Для отслеживания аналогичных обстановок в Гугл Analytics имеется инструмент «Последовательности», что разрешает выделить пользователей, каковые сперва были в одном сегменте, а после этого перешли в второй. Для анализа мы выстроили пара таких сегментов в Гугл Analytics:

И в следствии взяли такие цифры:

Согласно этой информации четко видно, что в сегмент REES46 из остальных перемещается очень большое количество пользователей. И это точно не баг, в противном случае пользователи перемещались бы между всеми сегментами равномерно.

Второй вывод: эти пользователи делают большое количество заказов.

*Вебмагазин подтвердил, что это настоящие заказы (практически все они имеют статус «выкуплено»)

По номерам заказов пользователей, перемещенных в сегмент REES46, мы изучили отечественные внутренние логи сессий и распознали следующие паттерны:

  1. Практически все пользователи, перемещенные в сегмент REES46, имеют добавление товаров в корзину (т.е. это более лояльная/конверсионная аудитория);
  2. Перемещения пользователей распределяются по часам неравномерно, это показывает на то, что оно инициируется вручную;
  3. Перемещения пользователей в сегмент REES46 происходит в те дни, в то время, когда Retail Rocket начинает побеждать в АБ тесте:


Перемещение пользователей в сегмент REES46 (сверху часы, слева дни)

Перемещение пользователей в сегмент Retail Rocket (сверху часы, слева дни)

В таблице видно, что 25 и 26 мая перемещений практически нет, а 27 мая, в то время, когда совокупность Retail Rocket начинает выходить в плюс — перемещения начинаются опять. И снова перемещаются пользователи, каковые додают товар в корзину и не так долго осталось ждать сконвертируются в клиентов.

Изучение кода, трудящегося на сайте

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

Оставалось два варианта: или кука изменяется сервером магазина Дочки Сыночки и этого не видно на клиенте, или динамическим кодом, что приходит с сервера по какому-то запросу.

Контролируя динамический код, мы искали а также функцию eval — особую javascript функцию, которая может делать любой текст, к примеру отправленный с сервера, как JavaScript код, что в недобросовестных руках разрешает скрыть функциональность кода, но наряду с этим дает полный доступ ко всему окружению сайта.

На протяжении проверки наткнулись на необычный кусок кода в JS библиотеке REES46:

Кусок кода из JS библиотеки REES46

Целый код дешёв по ссылке. Особенность этого куска кода — в нем очевидно пробуют скрыть его функциональность.

По коду возможно сделать пара выводов:

  • Данный фрагмент кода написан специально для магазина ДочкиСыночки, потому, что он скрыто применяет куку под именем “city”, принадлежащую магазину (магазин хранит в ней идентификатор региона пользователя)
  • Код намеренно написан так, дабы затруднить его понимание и чтение (вместо текста употребляются числовые идентификаторы букв)
  • Функциональность кода намерено прячется от внешних разработчиков — код не отрабатывает при открытой консоли браузера и для визитёров сайта из Москвы (вебмагазин обязан знать, что он интегрирует к себе на сайт, и какая строки кода за что отвечает, а тут — намеренное сокрытие)
  • Код рекомендован для загрузки картины с сервера REES46, раскодирования из данной картины текста, и передаче текста на вход в наивно запрятанную функцию eval (window[t(101) + «val»](u))
  • Все это говорит о возможности скрыто выполнить любой код со стороны REES46


Мы предполагаем, что когда это информация будет опубликована, REES46 удалит данный код, исходя из этого мы сохранили его посредством двух внешних свободных сервисов: https://web.archive.org и https://www.runscope.com

Его отформатированная версия доступна для изучения по ссылке.

Чтобы выяснить, что именно делает данный фрагмент, мы написали модуль, что эмулирует действия пользователя и логирует все запросы в сторону сервера REES46. 25 и 26 мая ничего не происходило (это кроме этого видно из таблицы с данными о почасовому перемещению пользователей в сторону REES46), а 27 мая, в то время, когда согласно данным Гугл Analytics совокупность Retail Rocket вышла в плюс по АБ тесту, около 7 вечера по Москве снова начались перемещения пользователей в сегмент REES46.

Перемещение пользователей в сегмент REES46 (сверху часы, слева дни)

Одвременно с этим мы зафиксировали запросы в сторону сервера REES46 на картину в формате PNG (содержимое картины возможно взглянуть по ссылке). Просто так картина не дешева (возвращается неточность 404), но при передаче в заголовке запроса к картине сессии пользователя REES46, картина выясняется дешёвой для скачивания:

В случае если картину передать на вход в код, что пробовали закодировать/скрыть, для удобства мы вынесли его раздельно, получается вот таковой JS, что изменяет значение куки, где хранится сегмент пользователя АБ теста:

document.cookie=rr-VisitorSegment_Rec=3:2; domain=.dochkisinochki.ru; path=/; expires=Mon, 25 Sep 2017 10:15:20 +0000;document.cookie=DS_SM_rrSegmentRecommendedABC=B; domain=.dochkisinochki.ru; path=/

Данный код очевидно изменяет две куки, которыми владел магазину, в которых хранится сегмент пользователя, на значение сегмента равного сегменту REES46.

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

Так, код совокупности REES46 перемещает в собственный сегмент пользователей, каковые добавили товар в корзину и вот-вот совершат заказ.

Согласно данным, взятым с момента начала логирования перемещений пользователей (1–28 мая), выстроенным на базе изначально выданного пользователям сегмента (другими словами из этих данных исключены все, кто в первый раз приходил на сайт до 1 мая), Retail Rocket точно побеждает в тесте, а REES46 сокращает продажи магазина:

Правильное окно миграции лояльных пользователей вебмагазина в сегмент REES46 неизвестно, исходя из этого отличие в эффективности намного больше.

Помимо этого, мы видим показатели вторых атак на тест в коде REES46, к примеру, при первом посещении сайта их совокупность осуществляет куки матчинг с несколькими RTB-сетями.

Код синхронизации:

Сохраненный запрос возможно взглянуть по ссылке на web.archive.org

Запросы синхронизации:

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

Интересный факт, что эта атака REES46 поддерживалась активной PR-кампанией в массмедиа и соцсетях:

Вместо заключения

За почти 5 лет работы, мы в первый раз сталкиваемся с подобным поведением. С сожалением нужно признать, что АБ тесты возможно проводить лишь при полной уверенности порядочности всех его участников.

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

UPD 23:00 2.06.17. Представители сервиса рекомендаций REES46 прокомментировали изучение следующим образом:

Мало вводной информации с отечественной точки зрения.

В начале теста на магазине Дочки-Сыночки мы нашли две вещи:

  1. Пользователи переходят из сегмента в сегмент.
  2. В магазине употребляется версия сегментатора, загружаемая с сервера Retail Rocket, а не с магазина (магазинная перезаписывается серверной).

Напрашивался вывод: Retail Rocket может осуществлять контроль сегментацию в собственную пользу.

Мы решили совершить собственный расследование. Для этого сделали функционал, отслеживающий переходы пользователей между сегментами и скрыли его код, дабы не допустить его преждевременное раскрытие.

На протяжении проверки обстоятельство была несложной: кука сегмента, проставляемая сегменатором Retail Rocket, удаляется по сроку судьбы или плагинами типа adblock, в итоге сегмент пользователя изменяется.

В первых числах Мая Retail Rocket отправил магазину письмо о том, что заказы утекают из их сегмента в отечественный. Мы сказали магазину собственную точку зрения: про устаревание cookie с сегментом и предоставили отчет с выгрузкой за день, где количество переходов между сегментами было приблизительно одинаково и составляло около 400 переходов за день. При посещаемости магазина часть таких переходов незначительна.

Представитель магазина данные принял.

Мы со своей стороны добавили функционал, восстанавливающий исходный сегмент визитёра, если он по какой-либо причине изменился. Метод действий: в случае если у пользователя установлена отечественная cookie и сохранен сегмент в отечественной БД и в очередном запросе к API сегмент изменился, мы восстанавливаем исходное значение сегмента.

На сегодня количество переходов выросло до 1500 в сутки. Обстоятельство кроме этого несложна: тест продолжается больше 5 месяцев, куки массово устаревают и очищаются. Функционал их восстанавливает.

В собственном доказательстве Retail Rocket приводит в пример код пикселя, что устанавливает сегмент B и это их основной довод. В запросе фигурирует код сессии пользователя: 708c0150-c562-4906-8f86-d7a64fa0663a

В отечественной базе существует история перехода этого пользователя между сегментами:

— {s1=3:2, s2=B, date=07-03-2017 12:42:05},

— {s1=3:3, s2=C, date=13-04-2017 19:13:59},

— {s1=3:2, s2=B, date=13-04-2017 19:14:06},

— {s1=3:1, s2=A, date=24-05-2017 16:37:05},

— {s1=3:2, s2=B, date=24-05-2017 16:39:20},

— {s1=3:3, s2=C, date=24-05-2017 17:00:58},

— {s1=3:2, s2=B, date=24-05-2017 17:15:01},

— {s1=3:3, s2=C, date=24-05-2017 17:15:13},

— {s1=3:2, s2=B, date=26-05-2017 15:43:24},

— {s1=3:1, s2=A, date=26-05-2017 16:04:58},

— {s1=3:2, s2=B, date=26-05-2017 16:08:00},

— {s1=3:1, s2=A, date=26-05-2017 16:08:47},

— {s1=3:2, s2=B, date=26-05-2017 16:40:38},

В случае если поменять этому пользователю сегмент, он будет восстановлен в сегмент B.

Если не поменять пользователю сегмент, пиксель не работает.

То же самое возможно сделать с любым вторым сегментом. Досточно зайти в режиме инкогнито на сайт, взять куку с сегментом, поменять ее на второе значение, прогуляться еще раз по сайту и сегмент восстановится в исходный. Независимо, каким был исходный: A, B либо C. Данный функционал будет трудиться на сайте еще несколько дней, дабы вы имели возможность его проверить.

Позже мы его отключим.

Пример для сессии: aebbf2c3-4e20-4a43-8091-9eca853bc577

— {s1=3:3, s2=C, date=16-05-2017 08:33:12},

— {s1=3:2, s2=B, date=16-05-2017 08:37:43},

— {s1=3:3, s2=C, date=16-05-2017 10:47:00},

— {s1=3:1, s2=A, date=16-05-2017 17:21:28},

— {s1=3:3, s2=C, date=16-05-2017 23:00:40}

Началось с С и закончилось им же.

Случайные статьи:

How do pregnancy tests work? — Tien Nguyen


Подборка похожих статей:

riasevastopol