В этом посте вы сможете узнать, почему даже с выключенным JavaScript и без плагинов, вы все равно можете отсылать поведенческие данные на сторонний сервер.
Также здесь мы рассмотрим метод, как получить поведенческую информацию от пользователей используя только HTML и CSS.
Возможно, после прочтения поста, вам покажется что я "изобрел колесо". Так и есть, методы описанные в этом посте не новы, и используют спецификации которые поддерживают практически все браузеры.
Так или иначе, эта информация поможет вам понять один нестандартный метод отслеживания поведения пользователей, который на данный момент нельзя "отключить" (в настройках) или заблокировать (плагинами вроде AdBlock или Ghostery).
Предыстория
Представьте на минуту, что у вас есть:
- Аудитория с выключенным JavaScript, или установлены плагины вроде Ghostery
- Желание отслеживать поведение пользователей
Прежде чем пытаться найти решение для этой задачи, давайте рассмотрим какими методами отслеживания мы располагаем на данный момент:
- JavaScript жучки, такие как Яндекс.Метрика, Google Analytics — действуют на стороне клиента.
- Анализаторы логов приложения, такие как logstash, awstat — оперируют логами приложения на сервере.
- Статичные счетчик — как правило, загружают скрытую картинку, или другой ресурс, не требуют выполнения JavaScript кода.
JavaScript жучки не подходят исходя из требований. За исключением таких, которые идут в комплекте с статичным счетчиком. К примеру, жучок для Яндекс.Метрики загружает изображение следующего вида:
<noscript><div><img src="//mc.yandex.ru/watch/XXXXXXXX" style="position:absolute; left:-9999px;" alt="" /></div></noscript>
В случае, если у клиента не выполняется JavaScript, этот подход позволит получить такую информацию, как:
- хиты
- хосты
- ip-адрес
- время визита
- … другие данные
Но есть одна проблема: вся информация носит статичный характер, то есть мы не получаем информацию о том, каким образом пользователь вел себя на странице.
Решение — используем CSS
В CSS мы можем загрузить внешний ресурс через инструкцию url(адрес-ресурса). Обычно этот ресурс загружается только тогда, когда он становиться необходим для рендеринга страницы. Почему бы не использовать эту особенность, для того чтобы собрать информацию о поведении пользователя? Мы вполне можем написать специальный CSS, который будет:
- собирать данные о поведении пользователя
- определять версии/особенности браузеров с помощью CSS-хаков
Итак, наша задача сводится к формированию HTML + CSS кода, который вынудит браузер, при взаимодействии с пользователем, сделать get запрос на наш сервер.
К примеру, мы желаем отслеживать клики по ссылкам. Для этого, мы можем использовать псевдокласс :active, а именно такой шаблон (jsfiddle):
/* <a class="spycss" href="https://google.com">spycss</a>*/
.spycss:active::after {
content: url("/backend/click-google");
}
При клике на такую ссылку, мы получим GET на /backend/click-google.
Похожим образом мы можем использовать и другие псевдоклассы:
.spycss1:hover::after {
content: url("/backend/hover");
}
.spycss2:focus::after {
content: url("/backend/hover");
}
В обоих случаях мы принимаем GET на нашем сервере.
Ускоряемся вместе с SpyCss
Писать такой CSS вручную и отслеживать каждую ссылку — довольно проблемно и непродуктивно. Именно для этих целей я написал небольшую библиотеку SpyCss. С помощью нее можно генерировать отслеживающий HTML + CSS без особых проблем. К примеру, можно использовать такой код для генерации отслеживаемой ссылки:
<?php
// Идентификатор пользователя, полезен для State-less бэкенда
$userId = 'get_from_cookie--OR--fetch_from_db';
// Адрес бэкенда, куда прийдут GET запросы.
$backendUrl = 'https://spy-css-backend/';
$s = new \SpyCss\SpyCss($userId, $backendUrl);
// Создаем ссылку формата
// <a class="scsssXXXX" href="https://hcbogdan.com">hcbogdan.com</a>
echo $s->builder()
->tag('a')
->content('hcbogdan.com')
->attribute('href', 'https://hcbogdan.com')
->interactions([
new SpyCss\Interaction\Active('click_on_hcbogdan_com')
])
->get();
// Библиотека генерирует CSS с необходимыми инструкциями
echo '<style>'.$s->extractStyles().'</style>';
Теперь мы можем отслеживать клики по ссылкам и наведения мышки на DOM элементы. Давайте посмотрим на HTML5 формы. А именно на валидацию (jsfiddle):
<form>
<input type="text" name="name" required />
</form>
<style>
.field:valid {
background: red;
}
</style>
Получается точно таким же образом мы можем использовать псевдокласс :valid для отслеживания заполнения формы:
// Создаем поле <input type="text" class="scsssXXXX" required />
echo $s->builder()
->tag('input')
->attributes([
'name' => 'you_name',
'value' => '',
'required' => true,
'placeholder' => 'Напишите текст',
])
->interactions([
new \SpyCss\Interaction\Valid('you_fill_input'),
])
->get();
echo '<style>'.$s->extractStyles().'</style>';
Когда пользователь заполнит поле — мы получим свой GET запрос.
Мы также можем отследить как долго пользователь держал курсор над DOM элементом (который получил состояние hover) с помощью css-анимаций. Например:
@keyframes spycss {
0% {background-image: url("/backend/0")}
100% {background-image: url("/backend/100")}
}
.spycss:hover::after {
animation: spycss 20s infinite;
}
Аналогичный пример с помощью библиотеки SpyCss (она добавит префиксы -webkit, -moz):
echo $s->builder()
->tag('a')
->content('hcbogdan.com')
->attributes([
'href' => 'https://hcbogdan.com',
'target' => '_blank'
])
->interactions([
new \SpyCss\Interaction\Online('view_on_hcbogdan_com'),
])
->get();
echo '<style>'.$s->extractStyles().'</style>';
Итоги
Даже с выключенным или недоступным JavaScript жучком, у нас есть с помощью CSS:
- отслеживать поведение пользователей,
- определять некоторые версии браузера
- определять примерные размеры окна и PPI
- определить ориентацию и тип устройства
В этом посте были рассмотрены некоторые, самые необходимые механизмы отслеживания. Полноценный пример, где используется все выше описанные подходы вы сможете найти тут.
Комментарии (48)
Vest
03.02.2018 02:45У меня к вам вот тут вопрос:
то есть мы не получаем информацию о том, каким образом пользователь вел себя на странице.
Вы же обрабатываете запросы на сервере. Уже как минимум отлавливаете переходы по своим ссылкам. События с формами также относятся к серверной активности.
Вариант с hover || focus мне не нравится потому, что я (как пользователь) смогу породить вам много паразитных запросов, например, зажав tab. Я думаю, что вашему серверу это не понравится, и мне как пользователю тоже.
Простите, писал с телефона, потому не форматировал текст.
MiXei4
03.02.2018 04:33Ссылка может быть внешняя и вы хотите знать кликают пользователи по ней или нет.
Сомневаюсь, что запрос будет происходить каждый раз при hover/focus. Скорее всего один раз на один элемент. И тут вы уже сами вольны распоряжаться на сколько элементов вешать такой обработчик.
hcbogdan Автор
03.02.2018 11:18Как правильно заметил MiXei4 это актуально для отслеживания как внутренних так и внешних ссылок.
По поводу форм, я согласен с вами — мы получим данные на сервере. Но цель же не просто отследить факт отправки формы, а понять как пользователь себя вел на странице. Допустим такой кейс:
У вас есть форма заказа, те пользователи которые ее заполнили и отправили — да мы приняли эту информацию. Но есть еще пользователи которые не прошли этот этап и на чем-то остановились. Цель поведенческой аналитики как раз в этом.
По поводу «смогу породить вам много паразитных запросов, например, зажав tab» — насколько я тестировал, вы не сможете такого сделать, запрос приходит лиш один раз за сеанс (после загрузки страницы) повторный фокус или клик — не создает запросов.
aamonster
03.02.2018 02:46Хм. А все эти url будут отрабатывать каждый раз или один раз скачаются и осядут в кэше?
Хотя, наверное, можно Cache-Control выставить...hcbogdan Автор
03.02.2018 11:23Есть некоторая особенность в обработке таких запросов.
Прежде всего все загрузки url(x) будут происходит один раз за сеанс (во множестве браузеров так). Другими словами, вы:
1. заходите на страницу spycss.hcbogdan.com
2. наводите курсор на ссылку google.com
3. на сервер отправляется GET
4. потом повторно наводите курсор на ссылку google.com
5. повторных запросов нет
Только если вы обновите страницу, или откроете в другой вкладке, вы сможете пройти путь снова с пункта №1.
Конечно нужно учитывать кэширование браузера, например можно использовать такие заголовки — github.com/Bogdaan/spycss-demo/blob/master/src/controllers.php#L181
altervision
03.02.2018 09:57-1Как думаете, а каким образом можно было бы с помощью CSS вытаскивать данные из форм? Например, из форм заказов на сворованных конкурентами лендингах. Существует ли вообще такая возможность?
hcbogdan Автор
03.02.2018 12:11Хм… Ответ зависит от того, в каком состоянии находиться форма.
Допустим, если у вас есть форма в которой сервер заранее устанавливает какие-либо значения (исходя из данных пользователя). В таком случае вы сможете загрузить эти данные к себе используя CSS.
Например, таким образом: jsfiddle.net/hcbogdan/1wdky4t6/1
hcbogdan Автор
04.02.2018 11:17Определенно, существует угроза «утечки» данных. Некоторую информацию вы можете найти в статье: создаем CSS кейлоггер
Methos
03.02.2018 11:24-3файл hosts
добавляем 127.0.0.1 mc.yandex.ru
и все ваши правила прекращают работатьMiXei4
03.02.2018 11:34Причём тут яндекс? Мы же можем все запросы отправлять на свой сервер. На тот же домен на котором расположен собственно сайт.
aamonster
03.02.2018 11:41hosts — халявный метод, идеален, если ненужный мусор на отдельном домене. Если он на том же домене, что сайт — опаньки. Но можно подавить с помощью extension. Для Chrome нужное API — chrome.webRequest onBeforeRequest, для Firefox, наверное, то же в browser. Но за всё приходится платить: это лишний код, выполняющийся перед загрузкой каждого файлика.
devalone
03.02.2018 14:07Current url schema:
/<user_id>/<endpoint>/<value>
и всё-таки лучше использовать сессию, а не id пользователя.hcbogdan Автор
03.02.2018 14:11Не согласен с вами по следующим причинам:
1. Запросы могут идти на другой домен. К примеру у вас сайт a.aa на нем загружены стили с домена b.bb
2. State-less бэкенд прост в реализации
3. Ничто не мешает вам игнорировать UserId, в случае, если ваш бэкенд поддерживает сесии (state-full, как например реализован spycss.hcbogdan.com). Но если у вас сессии не допустимы, данные потеряються.
riky
03.02.2018 22:39Пользователи отключающие js могут и картинки отключить. А если их отключить то ваше отслеживание уже не работает.
hcbogdan Автор
03.02.2018 22:46Не согласен с вами. Есть методы, которые я не описал в этой статье. К примеру загрузка внешнего шрифта директивой @font-face {src: url()}
Они работают даже тогда, когда картинки и JS отключен.LynXzp
03.02.2018 23:49uBlock блокирует загрузку внешних шрифтов. А про картинки, я подумал, вот про это:
JavaScript жучки не подходят исходя из требований. За исключением таких, которые идут в комплекте с статичным счетчиком. (пример яндекс счетчика)
Нормальные блокировщики (ну кроме nojs) отключают и то и то, и речи не идет чтобы оставить что-то одно. Конечно можно свою картинку запилить.
У меня подход простой: считаете меня на своем сайте — считайте на здоровье. Отправляете мои данные другим (яндексу, гуглу, ...) — обойдетесь. (Точнее обойдется яндекс и гугл).
Akuma
Покажите мне современный проект у которого есть «Аудитория с выключенным JavaScript», пожалуйста. Существенная аудитория, а не 2.5 киберманьяка-параноика.
Нашли? Отлично. А теперь чтобы сайт приэтом еще и не терял функциональности?
Нашли, но не можете ответить? Включите JavaScript
hcbogdan Автор
Вы в чем-то правы, как правило JavaScript включен у всех. Но существуют такие решения, которые работают без JS. Да и в целом — вы же не можете отключить или заблокировать CSS верно? Тогда весь сайт превратиться в простую разметку.
Я не призываю отказываться от JS-жучков, а только заполняю пробел между «JS-жучками» и «статическими счетчиками» (которые до сих пор идут в комплекте с JS-жучками в теге ).
С помощью методов описанных в статье вы сможете:
1. Обойти плагины блокирующие JS жучки, или uncaught exception.
3. Добавить еще один метод сепарации реальных пользователей от ботов phantomjs.
4. Если, ввиду особенности проекта, у ваших пользователей нет JS — анализировать их поведение.
ivan386
9 апреля эта система работать не будет.
Akuma
Ни разу не видел это в действии. Выкинуть день из прибыли?
ivan386
Целых два дня. Праздник проходит 48 часов. Он для того чтобы показать чего ваш сайт стоит без CSS. Здесь чуть больше: https://css-naked-day.github.io
Я периодически вижу сайты без css. Таблица стилей по каким то причинам не подгрузилась и получаем голый HTML. Причём и обновление иногда не помогает.
Akuma
Надо будет попробовать найти такие сайты 9 апреля :)
Правда я думаю, что сейчас они ничего интересного не покажут, т.к. табличной верстки уже нет нигде, а блоки без стилей — прямая линия вниз.
ivan386
Потому что надо использовать block и inline элементы правильно.
Akuma
Есть верстка на CSS-Grid, например.
Расскажите пожалуйста какие HTML элементы надо использовать, чтобы без CSS все осталось на своих местах?
ivan386
Если вам очень нужна сетка даже в случае когда отвалился CSS. То тут только таблицы использовать.
Сайт может быть не столь красивым без CSS но должен сохранять необходимую функциональность. Тоже касается и Javascript. Очень раздражают сайты на которых просто пустая страница без Javascript. Что сложного в том чтобы показать мне текст который поисковик нашол на этой странице без Javascript и CSS?
Недавно наткнулся на сайт который без аналитики гугла не хотел мне файл давать скачать. Не учли программеры что кто то её заблокировать может и в консоль сыпались ошибки.
daggert
> Сайт может быть не столь красивым без CSS но должен сохранять необходимую функциональность.
Зачем?
ivan386
Чтоб им могли пользоваться?
Areso
Внезапно некоторые посещают сайты с браузеров, где JS/CSS не поддерживаются by design. К примеру, если у меня сломалась DE, я использую Links (либо Lynx). Причем регулярно. И с серверов тоже пользуюсь. Впрочем, есть и графические браузеры, не поддерживающие JS/CSS.
Для меня самое главное на большинстве сайтов — это их наполнение, а не свистелки на JS и украшательства на CSS.
Более того, у меня время от времени чешутся руки написать граббер, который будет собирать с интересных мне страниц и сайтов только текст.
daggert
Согласен с вами, но только отчасти. Я тоже иногда захожу через такие браузеры и тоже граблю контент через скрипты (прокси-скрипты для сайтов где нет rss), однако может вам нужен не сайт без css, а сайт с хорошей семантикой?
Ок — можно прожить без JS — я сам ругаюсь на такие сайты, однако мне их приходится делать, но вот CSS… боюсь что это необходимый минимум. Правда я за то чтобы анимации можно было отключать.
Areso
Анимации? А видео, встроенное в фон страницы?) Ради одного этого я готов уфигачить CSS в ноль или использовать кастомное CSS всегда и везде.
daggert
Видео вроде как не в css, или я отстал от жизни?
Areso
Отстали.
www.myprovence.fr/snapshots2012/en
www.clinicadrmauro.com.br
Здесь у нас замечательная смесь CSS3, HTML5, video as background (трафик!).
у пэйпола было одно время видео в фоне, было у фейсбука, кажется у Эппл было…
daggert
Боюсь что вы не правы. Это просто контейнер video, а css тут только для выравнивания его по z-index'у и на нужной позиции.
Areso
Значит, я был неправ. Что не отменяет моего желания найти и покарать)
tutam
VolCh
Поисковики нынче и джс могут выполнять, и цсс анализировать.
ivan386
Я про то что минимум текст страницы должен быть доступен в любом случае. Даже если поисковики умеют полноценно обрабатывать страницу.
Jokerjar
Статья 2009 года. В то время, в эпоху табличной верстки, эта «шутка», может, и имела место: сайт становился «голым», но не терял свой, так скажем, скелет. Сейчас же, с блочной версткой, с адаптивом и со слоями, если снять CSS, сайт просто превратится в нечитаемую кашу.
Kicker
Боты phantomjs как раз обрабатывают js)
hcbogdan Автор
Конечно, но в таком случае нужно получать hover / focus и другие псевдоклассы — нужно потрудиться чтобы написать такой сценарий.
Если триггеры не срабатывают — вероятно это бот.
Areso
Встречаются люди с расширениями User CSS / Stylebot / Stylish / etc, которые могут к чертям сломать такую аналитику. У меня есть знакомые, которые их используют.
Goodkat
Я использую, чтобы отключать вырвиглазные шрифты, но описанные в статье приёмы я вряд ли замечу и отключу, даже случайно.
Впрочем, в последнее время я просто отключил левые шрифты в ublocker-e, и стили давно уже не подправлял.
Mihail72
тут, как мне кажется, вопрос про блокировку js способов различными плагинами.
Areso
Старые форумы работают отлично без JS.
Deosis
Есть расширения блокирующие скрипты по доменам.
ivan386
Noscript
ademaro
У всех есть JavaScript, да?
Akuma
Да никто ж не спорит. А еще могут свет выключить в момент покупки пользователем Ламборджини, а их сайт этого не предусматривает, вот беда то.
Давайте ориентироваться на нормальных пользователей, которых большинство.