Вступ
Я не знаю, чи знайдеться чи коли-небудь спосіб однозначно визначити машини за допомогою браузера. Основні причини:
- Вам потрібно буде зберегти дані на комп'ютері користувачів. Ці дані користувач може видалити будь-коли. Якщо у вас немає способу відтворити ці дані, які є унікальними для кожної машини, то ваша застрягла.
- Перевірка. Потрібно охороняти від підробки, викрадення сесії тощо.
Навіть якщо існують способи відстеження комп'ютера без використання файлів cookie, завжди знайдеться спосіб обійти його та програмне забезпечення, яке зробить це автоматично. Якщо вам дійсно потрібно відслідковувати щось на базі комп'ютера, вам доведеться написати нативну програму (Apple Store / Android Store / Windows Program / тощо).
Я, можливо, не зможу дати вам відповідь на поставлене вами питання, але я можу показати вам, як реалізувати відстеження сеансу. За допомогою відстеження сеансу ви намагаєтеся відстежувати сеанс перегляду замість комп'ютера, який відвідує ваш сайт. Відстежуючи сеанс, схема вашої бази даних буде виглядати так:
sesssion:
sessionID: string
// Global session data goes here
computers: [{
BrowserID: string
ComputerID: string
FingerprintID: string
userID: string
authToken: string
ipAddresses: ["203.525....", "203.525...", ...]
// Computer session data goes here
}, ...]
Переваги відстеження на основі сеансу:
- Для користувачів, які ввійшли в систему, ви завжди можете генерувати той самий ідентифікатор сеансу у користувачів
username
/ password
/ email
.
- Ви все ще можете відстежувати гостьових користувачів за допомогою
sessionID
.
- Навіть якщо кілька людей використовують один і той же комп’ютер (тобто кіберкафе), ви можете відстежувати їх окремо, якщо вони входять.
Недоліки сеансу відстеження:
- Сесії базуються на веб-переглядачах, а не на комп'ютері. Якщо користувач використовує 2 різних браузери, це призведе до двох різних сеансів. Якщо це проблема, ви можете перестати читати тут.
- Сесії закінчуються, якщо користувач не входить у систему. Якщо користувач не входить у систему, він використовуватиме гостьовий сеанс, який буде недійсним, якщо користувач видалить файли cookie та кеш браузера.
Впровадження
Існує багато способів здійснення цього. Я не думаю, що я можу їх все висвітлити, я просто перелічу свою улюблену, яка зробить це впевненою відповіддю . Майте це на увазі.
Основи
Я буду відстежувати сеанс, використовуючи те, що відоме як вічно печиво. Це дані, які автоматично відтворюють себе, навіть якщо користувач видаляє файли cookie або оновить свій браузер. Однак він не переживе, коли користувач видалить як свої файли cookie, так і кеш перегляду.
Для цього я використовую механізм кешування браузерів ( RFC ), API WebStorage ( MDN ) та файли cookie браузера ( RFC , Google Analytics ).
Юридичні
Щоб використовувати ідентифікатори відстеження, вам потрібно додати їх як до політики конфіденційності, так і до умов використання, бажано в підзаголовку Відстеження . Ми будемо використовувати наступні клавіші на обох document.cookie
та window.localStorage
:
- _ga : дані Google Analytics
- __utma : cookie для відстеження Google Analytics
- sid : SessionID
Не забудьте включити посилання на свою політику конфіденційності та умови використання на всіх сторінках, які використовують відстеження.
Де зберігати дані сеансу?
Дані сеансу можна зберігати в базі даних вашого веб-сайту або на комп'ютері користувачів. Оскільки я зазвичай працюю на менших сайтах (нехай 10 тисяч безперервних з'єднань), які використовують додатки сторонніх розробників (Google Analytics / Clicky / тощо), для мене найкраще зберігати дані на комп'ютері клієнтів. Це має такі переваги:
- Немає пошуку баз даних / накладних витрат / завантаження / затримки / простору / тощо
- Користувач може видаляти свої дані, коли захоче, без необхідності писати мені набридливі електронні листи.
і недоліки:
- Дані повинні бути зашифровані / розшифровані та підписані / перевірені, що створює накладні витрати на клієнта (не так вже й погано) та сервера (так!).
- Дані видаляються, коли користувач видаляє файли cookie та кеш. (цього я дійсно хочу)
- Дані недоступні для аналітики, коли користувачі виходять з мережі. (аналітика лише для користувачів, які зараз переглядають)
UUIDS
- BrowserID : унікальний ідентифікатор, сформований із рядка агента користувача браузера.
Browser|BrowserVersion|OS|OSVersion|Processor|MozzilaMajorVersion|GeckoMajorVersion
- ComputerID : Створений від користувачів IP-адресою та ключем сеансу HTTPS.
getISP(requestIP)|getHTTPSClientKey()
- FingerPrintID : відбитки пальців на основі JavaScript на основі модифікованого fingerprint.js .
FingerPrint.get()
- SessionID : Випадковий ключ, що створюється, коли користувач відвідує сайт.
BrowserID|ComputerID|randombytes(256)
- GoogleID : створено з
__utma
файлів cookie.getCookie(__utma).uniqueid
Механізм
Днями я переглядав шоу Венді Вілліамс зі своєю подругою і був зовсім зляканий, коли ведуча порадила своїм глядачам принаймні раз на місяць видаляти історію веб-переглядачів. Видалення історії браузера зазвичай має такі ефекти:
- Видаляє історію відвідуваних веб-сайтів.
- Видаляє файли cookie та
window.localStorage
(aww man).
Більшість сучасних браузерів робить цей варіант доступним, але не бояться друзів. Бо є рішення. У браузері є механізм кешування для зберігання скриптів / зображень та інших речей. Зазвичай навіть якщо ми видалимо нашу історію, цей кеш браузера все ще залишається. Все, що нам потрібно - це спосіб зберігати наші дані тут. Існує 2 способи зробити це. Краще використовувати зображення SVG та зберігати наші дані всередині своїх тегів. Таким чином, дані все ще можуть бути вилучені, навіть якщо JavaScript вимкнено за допомогою flash. Однак оскільки це трохи складніше, я продемонструю інший підхід, який використовує JSONP ( Wikipedia )
example.com/assets/js/tracking.js (насправді track.php)
var now = new Date();
var window.__sid = "SessionID"; // Server generated
setCookie("sid", window.__sid, now.setFullYear(now.getFullYear() + 1, now.getMonth(), now.getDate() - 1));
if( "localStorage" in window ) {
window.localStorage.setItem("sid", window.__sid);
}
Тепер ми можемо отримати наш ключ сеансу будь-коли:
window.__sid || window.localStorage.getItem("sid") || getCookie("sid") || ""
Як зробити так, щоб track.js дотримувався браузера?
Ми можемо добитися цього з допомогою Cache-Control , Last-Modified і ETag HTTP заголовки. Ми можемо використовувати SessionID
значення для заголовка етагу:
setHeaders({
"ETag": SessionID,
"Last-Modified": new Date(0).toUTCString(),
"Cache-Control": "private, max-age=31536000, s-max-age=31536000, must-revalidate"
})
Last-Modified
Заголовок повідомляє браузеру, що цей файл в основному ніколи не змінюється. Cache-Control
повідомляє проксі та шлюзи не кешувати документ, але повідомляє браузеру кешувати його протягом 1 року.
Наступного разу, коли браузер вимагає документ, він надсилатиме If-Modified-Since
і If-None-Match
заголовки. Ми можемо використовувати їх для повернення 304 Not Modified
відповіді.
example.com/assets/js/tracking.php
$sid = getHeader("If-None-Match") ?: getHeader("if-none-match") ?: getHeader("IF-NONE-MATCH") ?: "";
$ifModifiedSince = hasHeader("If-Modified-Since") ?: hasHeader("if-modified-since") ?: hasHeader("IF-MODIFIED-SINCE");
if( validateSession($sid) ) {
if( sessionExists($sid) ) {
continueSession($sid);
send304();
} else {
startSession($sid);
send304();
}
} else if( $ifModifiedSince ) {
send304();
} else {
startSession();
send200();
}
Тепер кожен раз, коли браузер запитує tracking.js
наш сервер, буде відповідати 304 Not Modified
результатом і змушувати виконувати локальну копію tracking.js
.
Я досі не розумію. Поясніть це мені
Припустимо, користувач очищає історію перегляду та оновлює сторінку. Єдине, що залишилося на комп'ютері користувачів - це копія tracking.js
кешу браузера. Коли браузер вимагає, tracking.js
він отримує 304 Not Modified
відповідь, яка змушує його виконати першу tracking.js
отриману версію . tracking.js
виконує та відновлює те, SessionID
що було видалено.
Перевірка
Припустимо, Haxor X викрадає файли cookie наших клієнтів, поки вони все ще ввійшли в систему. Як ми їх захищаємо? Криптографія та відбитки пальців браузера на допомогу. Запам’ятайте наше первісне визначення для SessionID
:
BrowserID|ComputerID|randomBytes(256)
Ми можемо змінити це на:
Timestamp|BrowserID|ComputerID|encrypt(randomBytes(256), hk)|sign(Timestamp|BrowserID|ComputerID|randomBytes(256), hk)
Де hk = sign(Timestamp|BrowserID|ComputerID, serverKey)
.
Тепер ми можемо перевірити наше SessionID
використання наступного алгоритму:
if( getTimestamp($sid) is older than 1 year ) return false;
if( getBrowserID($sid) !== createBrowserID($_Request, $_Server) ) return false;
if( getComputerID($sid) !== createComputerID($_Request, $_Server) return false;
$hk = sign(getTimestamp($sid) + getBrowserID($sid) + getComputerID($sid), $SERVER["key"]);
if( !verify(getTimestamp($sid) + getBrowserID($sid) + getComputerID($sid) + decrypt(getRandomBytes($sid), hk), getSignature($sid), $hk) ) return false;
return true;
Тепер для того, щоб атака Хаксора спрацювала, вони повинні:
- Мають те саме
ComputerID
. Це означає, що вони повинні мати того самого постачальника провайдерів, що і жертва (Tricky). Це дасть нашій жертві можливість вжити законних дій у власній країні. Haxor також повинен отримати ключ від сеансу HTTPS від жертви (Hard).
- Мають те саме
BrowserID
. Будь-хто може підробити рядок User-Agent (Дратівливо).
- Вміти створювати власну підробку
SessionID
(Дуже важко). Томові атаки не працюватимуть, оскільки ми використовуємо часову марку для генерування ключа шифрування / підпису, так що, як генерування нового ключа для кожного сеансу. Крім того, ми зашифровуємо випадкові байти, тому просту атаку словника також не виникає.
Ми можемо покращити перевірку за допомогою переадресації GoogleID
та FingerprintID
(через ajax або приховані поля) та порівняння з ними.
if( GoogleID != getStoredGoodleID($sid) ) return false;
if( byte_difference(FingerPrintID, getStoredFingerprint($sid) > 10%) return false;