Тот способ основывается на использовании метода DOM элемента getComputedStyle. Будучи вызванным у HTMLAnchorElement, он позволяет различать :visited и обычные состояния ссылок на популярные сайты.
Баг давно закрыли и больше им воспользоваться нельзя.
Мой метод основывается на том факте, что favicon.ico посещаемых пользователем сайтов, скорее всего, будут находиться у него в кэше и, соответственно, загрузятся быстрее, чем тех сайтов, на которых он ни разу не был. Браузеры очень агрессивно кэшируют favicon.ico, что лишь увеличивает надежность такого способа.
Ниже приведен полный исходный код proof-of-concept реализации этого способа. С его помощью можно продемонстрировать, что вы посещаете сайт habrahabr.ru, но ни разу не были на сайте hornet.com.
var diffTreshold = 200; // Порог времени, который необходимо преодолеть, чтобы считать, что пользователь посетил сайт.
var body = document.querySelector('body');
var testResults = [];
var testCases = [
'hornet.com', 'habrahabr.ru'
];
testCases.forEach(test);
function test (host) {
var start = new Date();
var img = new Image();
img.src = 'http://'+host+'/favicon.ico';
img.onload = function () {
saveResult(host, start, new Date());
}
body.appendChild(img);
}
function saveResult (host, start, end) {
var diff = end - start;
testResults.push({
host: host,
start: start,
end: end,
diff: diff,
visited: diff <= diffTreshold
});
}
Этот код дает не очень точные результаты, потому что использует константу diffTreshold, которая подбирается эмпирически. Эта переменная — количество миллисекунд, прошедших с начала загрузки картинки до ее окончания, которое следует считать попаданием в кэш браузера.
Более точный метод должен основываться на подсчете среднего значения между минимальным и максимальным временем загрузки картинки, при этом, одна из ссылок должна вести на гарантированно несуществующую в кеше иконку сайта. Тогда все, что меньше этого среднего значения, можно считать попаданием в кеш и, следовательно, свидетельствовать о том, что пользователь был на указанном сайте.
Этот способ имеет также и один нерешенный мной недостаток: после первого такого теста его последующие запуски сделают результаты бесполезными.
Может показаться, что сама необходимость такого сниффинга истории сомнительна, но для владельцев интернет-магазинов или посадочных страниц он может быть полезен, чтобы показать пользователю релевантную рекламу, основываясь на знании о том, какие сайты конкурентов или партнеров он уже посещал.
UPD:
Удивило, чтот пост стал достоин инвайта. Видимо, это действительно очень интересная тема. Попробую реализовать этот метод в виде готовой библиотеки.
Комментарии (19)
nickolaym
29.05.2015 12:40+3Произвольная иконка для страницы
<link rel="shortcut icon" src="fig-vam-a-ne-favicon.ico" />
Или проверка реферерра при запросе favicon
RewriteCond %{HTTP_REFERER} ^(?!your.domain.com)(.+)$ RewriteRule ^favicon.ico$ [R=404,L]
Ибо нефиг!shergin
29.05.2015 20:14Описанные вами способы защиты работать не будут. Правильным решением было бы использовать Content Security Policy.
Xu4
02.06.2015 15:31Проверкой реферера вы только облегчите работу этому скрипту. Если в браузере в кэше есть «favicon.ico», она нормально отдастся скрипту. Если в кэше её нет, вернётся ошибка 404, которую отловить легче, чем экспериментировать с «threshold».
Ogra
29.05.2015 13:13+20var diffTreshold = 200; // Порог времени, который необходимо преодолеть, чтобы считать, что пользователь посетил сайт.
visited: diff > diffTreshold
Ну наоборот же, елы-палы. Преодолели порог, значит не посещали сайт. Вы свой froof-of-concept сами хоть проверяли?
Ну и по мелочи:
saveResult(host, start, new Date() — забыли закрывающую скобку;
threshold, а не treshold;
YChebotaev Автор
29.05.2015 21:22Прошу прощения за ошибки и опечатки, сам удивился, как это у меня так получилось.
CAH4A
29.05.2015 14:06+6Если сделать достаточное количество запросов, то можно даже попытаться применить кластерный анализ, вместо непонятно что означающего «среднего значения между минимальным и максимальным временем загрузки».
А ещё, можно делать два запроса: на favicon.ico и на favicon.ico?randomhashhere.
Тогда можно знать за сколько скачивается та же фавиконка, но без кеша.YChebotaev Автор
29.05.2015 21:14Я не думаю, что делать свое решение для кластерного анализа будет целесообразно в каждом конкретном случае. Однако, его можно попытаться продать как отдельный сервис или стать частью более крупного решения.
CAH4A
29.05.2015 22:25Тут отлично подойдёт самый простой алгоритм «k-средних» (а у нас тут k=2), да и пишется он на коленке за пол часа.
И для каждого конкретного случая писать его снова не нужно. В том и прелесть, если сравнению с Threshold=200.
Продать «k-средних» как отдельный сервис, это к маркетологам. -)YChebotaev Автор
29.05.2015 22:52Насколько я понимаю, к-средних может быть все равно не точным, если среди тестовой выборки нет гарантированного промаха и попадания в кеш. Потому что без наличия таких калибровочных значений в выборке, алгоритм кластеризации не сможет разделить случаи, когда вся выборка принадлежит к одному из двух классов. С этой точки зрения, предрассчитанная пороговая константа работает лучше.
На моем компьютере, скорость кэшированной картинки редко превышает 10 миллисекунд, а скорость загрузки с сервера редко быстее 100 миллисекунд. Тут нужно еще учитывать, что раз пользователь не был на сайте, то браузер, скорее всего, будет еще и dns lookup делать.
stifff
29.05.2015 14:32+4«боливар не выдержит двоих.»
Такой трюк только один раз можно будет провернуть.
akirsanov
29.05.2015 16:04+2Интересно!
Также есть замечательное исследование тайминг атак с BH-13 media.blackhat.com/us-13/US-13-Stone-Pixel-Perfect-Timing-Attacks-with-HTML5-WP.pdf
Ребята заметили, что отрисовка элементов занимает некоторое время, увеличили это время с помощью фильтров и выводили среднее время задержки рендеринга для посещенной и непосещенной ссылки. На данный момент затронутая ими проблема пофиксена в ff/chrome/ie.Bo0oM
29.05.2015 21:49Насколько я знаю, там было не детектирование посещения, а считывание содержимого страницы.
Не знал, что это собираются фиксить =)
В копилку, есть детектор социалок от bushwhackers. Еще прошу обратить внимания на детект посещения с помощью HSTS :)akirsanov
30.05.2015 17:13Там было и детектирование посещения по времени отрисовки, и распознавание элементов страницы по view-source во фрейме при наложении фильтров, — и все это пофиксено в 13 году.
amarao
29.05.2015 18:31+5Для тех, кого раздражают сайты, качающие что-то с других сайтов без вашего разрешения (например, как в этом посте):
addons.mozilla.org/ru/firefox/addon/requestpolicy
Куда менее агрессивный, чем noscript, и после минимальной настройки позволяет практически исключить любые подобные трюки.
xana4ok
30.05.2015 03:29Это только половина дела. Надо взять топ 100000 сайтов по версии quantcast с демографией.
Rastishka
Идея классная!
Но не хватает примера с десятком сайтов для теста.