Https или защищаем web
Способы идентификации в интернете статья тут
Как идентифицировать посетителя?
Когда-то давно для этой задачи были придуманы Cookie.
Однако есть гораздо больше способов идентификации. Многие технологии так или иначе передают информацию о пользователе и компьютере.
Все это позволяет "узнавать" даже посетителя, который специально удалил куки, сменил браузер и перешел на другой IP.
Примеры
Некоторые примеры подобных сервисов есть в интернете.
Panopticlick
Сайт http://panopticlick.eff.org/ использует открыто предоставляемую браузером информацию для идентификации. Ему не нужны Cookie.
Просто зайдите на него и нажмите красную кнопку "Test Me". Мой браузер - уникален. А ваш?
Evercookie
Библиотека Evercookie на http://samy.pl/evercookie/ позволяет сохранять информацию, используя HTML5, Flash и другие средства. Удалить ее, просто почистив Cookie, невозможно.
И не только...
Ресурсы выше - так, детские игры.
Возможностей по идентификации гораздо больше. Многие из них вы найдете в этой статье. Во всяком случае, полнее обзора я не встречал. Даже просили не публиковать
Как можно использовать?
На ум приходит как минимум несколько применений.
Реклама.. "Петя, это ты… Ну и что, что куки отключены… Купи слона!" Бан.. "Вася, тебе же сказали. За-ба-нен! И прекрати менять IP/браузеры." Наблюдение и сбор информации.. Большой брат смотрит за тобой. Подозреваю, что могут быть и другие примеры использования.
Компоненты цифровой подписи
Различные технологии позволяют серверу получать информацию о браузере и компьютере.
Фрагменты этой информации образуют подпись, которая, как вы наверно уже видели на http://panopticlick.eff.org/, позволяет успешно идентифицировать компьютер.
Работа с Cookie
Да, Cookie - это "old school". С точки зрения идентификации - работают, но не фонтан. Вася знает про куки. Злой тролль знает про куки и может их удалить или отключить..
Но интересно то, что иногда их и поставить - проблема. Даже если удалять никто не собирается.
3rd party cookie
Рассмотрим ситуацию... На странице установлен скрипт или даже iframe. Может ли он поставить свои cookie, и тем самым пометить посетителя?
А перед тем, как вы скажете "да, разумеется, любой HTTP-ответ может поставить cookie" - небольшой пример.
Пример
Вот iframe. Она загрузит iframe, который ставит cookie в заголовке и в javascript, а затем выведет текущие cookie на экран.
<?php setcookie('sid', rand(),0,'/'); ?><!DOCTYPE HTML> <html> <head> <script>
document.cookie = 'sid=' + Math.random() + ';path=/' alert(document.cookie)
</script> </head> <body></body> </html> Вот страница для просмотра этого ифрейма. Запустите ее в IE или Safari:
<!DOCTYPE HTML> <html> <body>
<iframe src="http://ilyakantor.ru/js/3rd/frame.php" height="100"></iframe>
</body> </html> При настройках браузера по умолчанию, вы должны увидеть пустой алерт. Это означает, что кука не поставилась.
Дело в том, что cookie с других доменов имеют особое название "3rd party cookie" и особое отношение со стороны браузера.
Браузеры можно настроить на запрет таких Cookies. Но пример выше не ставит куку даже при настройках по умолчанию. В чем дело?
P3P
IE не ставит 3rd party cookies без специальной политики P3P, которую можно передать в серверных заголовках. Эта политика показывает, в каких целях вы "метите" пользователя, что вы собираетесь делать с информацией о нем в дальнейшем.
Здесь все довольно просто - сервер с другого домена должен добавить заголовок, можно вот такой:
header('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"'); После этого IE будет радостно ставить переданные куки.
Обход блокирования 3rd party cookies
... Но это не все. Настройки Safari по умолчанию запрещают прием 3rd party cookies. Аналогичную опцию можно включить и в ряде других браузеров.
Хорошо если такие куки вам не обязательны, а если они - важная часть системы, например, кросс-доменной авторизации?
Чтобы обойти засаду, нужно дать браузеру понять, что ваша кука - не какая-то там "левая", а что пользователь действительно осуществляет навигацию по ифрейму.
Для этого достаточно отправить в iframe POST-запрос, и в ответ на него сервер с другого домена уже может передать cookie, которые браузер примет как родные.
Код фрейма:
<?php setcookie('sid', time(),0,'/'); header('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');
?><!DOCTYPE HTML> <html> <head> <script>
alert(document.cookie)
</script> </head> <body></body> </html> Код страницы для просмотра в Safari или другом браузере с запретом 3rd party cookies:
<!DOCTYPE HTML> <body> <script>
onload = function() {
var div = document.createElement('div') document.body.appendChild(div)
div.innerHTML = '<iframe src="" name="iframe"></iframe> \
<form method="POST" target="iframe" action="http://ilyakantor.ru/js/3rd/frame_hack.php"></form>'
div.getElementsByTagName('form')[0].submit()
}
</script> </body> Попробовали? Должно быть все в порядке.
И еще пара заметок.
Строго говоря, это не совсем хак, ведь такой iframe - не чужой внешнему окну. В него происходит POST.. А значит, скорее всего такой подход будет работать и далее. P3P-политика будет работать и для других объектов: подключаемых скриптов, картинок и так далее. Но... Только в IE. Другие браузеры ее не уважают, так что для Safari пока только iframe. Javascript
С помощью javascript из браузера можно извлечь массу всего интересного. И не только извлечь, но и сохранить.
Browser Persistency
Этим ёмким словом называются различные варианты сохранения информации в браузере.
Если подробнее:
userData (IE*) HTML5 localStorage (все современные браузеры) HTML5 database (Chrome, Safari, Opera) Всё это позволяет сохранить информацию в браузере, которая, в отличие от cookie, не пересылается на сервер при HTTP-запросах.
Более подробно о технологии вы можете почитать на сайте http://browserpersistence.ru/ и в статье Хранение данных на клиенте. DOM Storage и его аналоги..
Пример - ниже. Там iframe с адреса http://javascript.ru/files/id/persistence.html, по нажатию на Save сохраняет данные из текстового поля во все доступные хранилища, по Load - восстанавливает.
Кнопка Refresh перегружает страницу. А впрочем, можно и браузер переоткрыть, разницы не будет.
navigator
С помощью Javascript доступен замечательный объект nagivator, содержащий полную информацию о браузере посетителя и название его OS.
<script> for(var prop in navigator) { if (navigator[prop] instanceof Object || navigator[prop]===) continue document.writeln(prop+": "+navigator[prop]) } </script> Многие из этих свойств не изменяются даже при обновлении мажорной версии браузера, не говоря уже минорной. screen Для полноты картины все об экране посетителя расскажет объект screen.<script> for(var prop in screen) { if (navigator[prop] instanceof Object || screen[prop]===) continue document.writeln(prop+": "+screen[prop]) } </script> Плюс этой информации - в том, что этот компонент подписи очень стабилен и не меняется почти никогда. navigator.plugins Для всех браузеров, кроме IE, этот объект содержит информацию о плагинах и их версии. Посмотрите сами:<script> var prev for(var i=0;i<navigator.plugins.length;i++) { var plugin = navigator.plugins[i] var plugin = plugin.name+" "+(plugin.version || ) if (prev == plugin ) continue document.writeln(plugin ) prev = plugin } </script> Вася обновил браузер? Ничего! Большинство плагинов по-прежнему такие же и тех же версий. Плагины IE В IE все сложнее. Плагины там являются компонентами ActiveX, поэтому получить их список нельзя. Однако, есть workaround - обернуть создание компонентов в try..catch: try { new ActiveXObject('плагин') // плагин есть } catch(e) { // плагина нет } Все несколько сложнее, если нас интересует еще и версия, однако делается без особых проблем, и даже без VBScript. Замечательная статья по этому поводу: Detecting plugins in Internet Explorer. Обнаруживаемые плагины: Adobe Reader Flash Player Java Runtime Environment QuickTime Player RealPlayer Shockwave Player Windows Media Player Весьма внушительный список, который можно даже немного порезать - и все равно будет неплохо. Date Объект Date содержит временную зону посетителя. alert( -new Date().getTimezoneOffset()/60 ) Это еще один стабильный компонент для идентификации. ActiveX (IE) Потенциально ActiveX может очень многое. Но при обращении к большинству из них браузер запросит подтверждение у посетителя. Для идентификации, особенно скрытой, такое явное раскрытие себя ни к чему. Однако, получить список системных шрифтов при помощи ActiveX - проще пареной репы. И без всяких вопросов. Следующий пример будет работать, только в IE. <object id="dialogHelper" classid="clsid:3050F819-98B5-11CF-BB82-00AA00BDCE0B" width="0px" height="0px"></object> <script> onload = function() { var dlg = document.getElementById('dialogHelper') var fonts = [] for (var i=0; i<dlg.fonts.count; i++) { fonts.push('' + dlg.fonts(i+1)+ '') } document.write(fonts.join("")) } </script> Flash Local Storage Как известно, у Flash есть свое локальное хранилище, в которое можно записывать данные и доставать их оттуда. При этом Flash может замечательно взаимодействовать с Javascript, см. статью Javascript <-> Flash мост. То есть, самый простой способ - это сохранить идентификатор посетителя и потом проверять его по необходимости. Важно, что в отличие от методов хранения HTML 5, flash-хранилище привязано не к домену страницы, а к домену, с которого загружено flash-приложение. Иначе говоря, flash'ка с домена http://ads.com, находясь на странице http://a.com и http://b.com имеет одинаковое хранилище. Даже если эти страницы открывают в разных браузерах - хранилище разделяется, т.к. привязано не к браузеру, а к пользователю компьютера (на уровне OS). Сведения о системе Для получения сведений из Flash можно использовать объекты: system.Capabilities text.Font ..а также некоторые другие. Таким образом можно получить: Сведения об экране. Сведения об операционной системе и ее возможностях. Список системных шрифтов. Всю эту красоту можно передать либо в Javascript, либо напрямую на сервер. Javascript отключен? Flash работает даже с выключенным javascript. Из Flash можно проверить, включен ли JS и, при необходимости, связаться с сервером напрямую, используя сокеты: "Тук-тук, я флеш, под IP таким-то сейчас сидит Вася..." Java! ...Барабанная дробь.. На сцену выходит Java! UPDATE 28.02.2011: До версии 6.22 Java могла выдавать MAC-адрес и внутренние адреса. Сейчас эту возможность убрали, но можно, например, собрать все внутренние имена адаптеров, а также узнать сетевое имя, информацию о памяти, процессоре, шрифтах и т.п. При этом Java может как передать данные в Javascript, так и самостоятельно отправить POST-запрос с информацией. Часть статьи ниже не будет работать на последней Java. Будет переписано. Получение MAC и IP Здесь нужно немного углубиться в детали. Дело в том, что в интеренет есть много примеров на эту тему. Но, как правило, они не работают. Вот это можно встретить везде (Firefox only?): var location = window.location var address = (new java.net.Socket(location.host, location.port || 80)).getLocalAddress() alert(address) Как видно, доступ к java идет напрямую из javascript, то есть не нужно загружать апплет. У меня это сработало только в Firefox А вот - тот же пример, но с получением MAC-адреса. var location = window.location var address = (new java.net.Socket(location.host, location.port || 80)).getLocalAddress() var mac = java.net.NetworkInterface.getByInetAddress(address).getHardwareAddress() var s = for(i=0;i<mac.length;i++) { // привести byte[] к читаемому виду var n = mac[i] if (n<0) n = 256+n s += n.toString(16) } alert(s) Что делать в других браузерах? Да просто загрузить Java-апплет (код будет дальше). Но!.. И тут есть одно "Но". Браузер Safari заботится о посетителях. Как мы видели выше, он по умолчанию не принимает 3rd party cookies. И в отношении Java он весьма суров: попытка доступа к сокету вызовет "Permission denifed": // этот java-код не сработает Socket socket = new java.net.Socket(getCodeBase().getHost(), port); InetAddress address = socket.getLocalAddress(); Таким образом, получить текущий адрес, его IP и MAC адаптера не получится. Но, с другой стороны, никто не мешает получить все доступные адаптеры, их MAC и IP, вот так: Enumeration<NetworkInterface> niEnum = NetworkInterface.getNetworkInterfaces(); while (niEnum.hasMoreElements()) { NetworkInterface ni = niEnum.nextElement(); if (ni.getHardwareAddress() != null && !ni.isLoopback() && ni.isUp()) { // MAC - в ni.getHardwareAddress() } } Демо Если у вас установлена Java, то в iframe ниже вы увидите свой MAC-адрес. Для простоты тут пропущено получение IP. Добавить его, как и данные по памяти, процессору и т.п. - весьма несложно. Исходник: java.zip Другие способы CSS без JS Есть ли хоть одна технология, которая "не стучит". Первая мысль была - CSS. ... Как я ошибался. Современный CSS поддерживает media-query, то есть условия на: device-width device-height device-aspect-ratio color-index Использовать это очень просто. Достаточно добавить несколько условий вида: @media screen and (min-device-width: 0px) { #device-width { background: url(/forum/media.php?sid=...&device-width=0); } } } @media screen and (min-device-width: 800px) { #device-width { background: url(/forum/media.php?sid=...&device-width=800); } } @media screen and (min-device-width: 1024px) { #device-width { background: url(/forum/media.php?sid=...&device-width=1024); } } @media screen and (min-device-width: 1280px) { #device-width { background: url(/forum/media.php?sid=...&device-width=1280); } } @media screen and (min-device-width: 1440px) { #device-width { background: url(/forum/media.php?sid=...&device-width=480); } } Как видно, условия перекрывают друг друга, и в каждом указана минимальная ширина экрана. Например, при экране 1280px будет использован фон media.php?key=...&device-width=1280. Иначе говоря, на сервер отправится запрос, который скажет, что у посетителя экран - 1280 пикселей. При этом параметр sid может использоваться для сессии при отключенных cookies. Да, это не внутренний IP, не МАС, но оно работает с отключенными JS и Cookies. А это уже чего-то стоит, информация стабильная и очень конкретная. Как компонент цифровой подписи - в самый раз. CSS + History + JS Можно ли использовать History для хранения идентификатора посетителя? Да, но не везде. Для этого вспомним, что CSS-ссылка посещенная отличается от непосещенной по цвету. Можно создать ссылку с нужным href и, используя getComputedStyle получить цвет, т.е. выяснить, посещал ли пользователь ссылку. Любой идентификатор можно представить в двоичном виде, например 867865 = 11010011111000011001. Для записи идентификатора - создаем iframe и последовательно постим туда адреса вида /visited/0/1, /visited/1/0, /visited/2/0, /visited/3/1 .. и так далее, с конца бинарного числа к началу. Так что для каждого бита будет посещена одна из ссылок: /visited/n/1 или /visited/n/0. Для загрузки идентификатора достаточно создать такие ссылки и проверить, какие из них посещены и, таким образом, восстановить число. 32 ссылки - это 32-битный идентификатор, его хватит примерно на 4 миллиарда посетителей. Надо сказать, что такой способ доступа к History через CSS известен уже много лет, и дыра постепенно закрывается браузерами, см. например https://bugzilla.mozilla.org/show_bug.cgi?id=147777. IP С IP все просто, но не совсем. Есть ведь динамические IP. Что если Вася поменяет динамический адрес, как его отследить? А очень просто - записывать в подпись не только сам IP, а провайдера, который его выдал. Так что переподключение Васю отчасти скроет (если ип другой), но провайдер в подписи останется тот же, это упростит идентификацию. Также по базе GeoIP можно получить регион и страну. Да, это не 100% надежно, но подпись - компонентная. Каждый компонент может быть неуникален, может меняться, но все вместе они однозначно идентифицируют посетителя. HTTP-заголовки Замечательный и очевидный источник информации: User-Agent Accept Keep-Alive Via (proxy) Редкие заголовки Заголовок User-Agent может содержать не только информацию о браузере, но и OS, плагины, детали сборки. Для более надежной обработки User-Agent можно распарсить на компоненты и использовать их как независимые части подписи, чтобы обновление одного из плагинов не сильно на ней отразилось. Кэш браузера Использовать кэш браузера можно различными способами. Самый простой - ETag. При обращении к странице сервер выдает ETag, который браузер использует для кэширования содержимого. При последующих запросах он отправляет этот ETag на сервер, который, таким образом, узнает, кто к нему пришел. В iframe ниже - ваш уникальный ETag. Обратите внимание - даже при перезагрузке страницы по Ctrl-F5 кэш ифрейма не очищается! То есть, сервер вас по-прежнему будет узнавать, проверьте сами... Браузерные фичи Идентифицировать браузер можно и при заведомо кривом UserAgent'е. Достаточно посмотреть поддержку различных фич. Причем, для многих из них даже javascript включать не обязательно. Количество одновременных HTTP-соединений Поддержка link prefetching Поддержка conditional comments CSS Hacks and Features ... Скрыть свой браузер и его версию при грамотной проверке фич довольно сложно. TCP TCP-протокол с радостью предоставит информацию о вашей операционной системе. Дело в том, что в различных OS по-разному настроен TCP-стек. А роутер, как правило, не меняет пакет, а просто передает его дальше. Характеристики TCP-пакетов формируют свой фрагмент цифровой подписи. Для его распознавания проще всего применить утилиту p0f. Работает очень просто. Вешается как фильтр пакетов и слушает сокет. Сохраняет TCP-сигнатуры пакетов в кеш в памяти, держит их N секунд. Веб-сервер отправляет на сокет сообщение "А кто это только что зашел под IP X.Y.Z ?" p0f выдает OS, которую вычисляет по своей базе из сигнатуры (можно пропатчить, чтобы выдавал саму сигнатуру). Общий алгоритм Из всей этой красоты, описанной выше, строится компонентная цифровая подпись. Алгоритм на сервере умеет сравнивать подписи. Пример такого алгоритма вы можете увидеть в статье на https://panopticlick.eff.org/ : How Unique is Your Browser?. Но Pantoptclick - открытый проект. Он использует лишь небольшую часть приемов, описанных в статье, и при этом - весьма эффективен. Реальный алгоритм может быть сложнее и гораздо (в десятки и сотни раз) эффективнее. Как скрыться? Итак, мы поговорили о том, как идентифицировать посетителя. А теперь - поговорим, как скрыться. Способов идентификации очень много. "Стучит" буквально все. Одного простого средства типа "privacy mode" в браузере для скрытия недостаточно. Нужно одновременно использовать несколько защит: Privacy/incognito mode Закроет возможность идентификации через кеш и различные виды browser storage. Желательна последняя версии Flash, т.к. хранилище ранних версий не интегрировано с браузером. NoScript Плагин для Firefox. Перекроет запуск скриптов, апплетов, flash, silverlight и другое активное содержимое. Таким образом будет закрыты механизмы идентификации через javascript, java, flash. Жаль, для других браузеров такого нет. Клиент к сети Tor, в частности расширение TorButton для Firefox Ваши запросы будут приходить с разных IP. Некоторые ресурсы, например Wikipedia, блокируют Tor. Локальный proxy, который правит заголовки. Заголовки типа User-Agent, Accept и т.п. ни в коем случае нельзя резать. Если браузер - Firefox (это можно обнаружить по браузерным фичам), а User-Agent порезан или IE - это выделит из толпы самым ярчайшим образом. Самое надежное - это поставить самые типичные заголовки для в вашей версии браузера. А любой нестандарт - убрать. Слиться с толпой. При аккуратном применении этих мер - вас все равно можно будет как-то идентифицировать. Например, отключенный javascript, отключенные cookies - это выделяет из толпы. Tor - тоже паттерн (запросы с разных IP в рамках одной сессии). Но фингерпринтинг - вероятностный метод. Он часто позволяет идентифицировать посетителя с вероятностью, близкой к 100%, но eсли посетитель предпринимает серьезные усилия по анонимизации, а общее количество посетителей очень большое, то эта вероятность падает. Реально достичь значения - 1 из 50.000. Для ресурсов с сотнями тысяч посетителей такое посещение достаточно анонимно. P.S. После публикации статьи порекомендовали еще behavior clientCaps как источник информации, но вся информация оттуда доступна через navigator и screen, так что в статью этот источник не добавлен.