Неблокируемый серверный фингерпринтинг



Итак, давайте признаем честно. Все используют фингерпринтинг и трекеры, и все используют adblock и прочие no-track плагины. Эта война будет вечной, компаниям нужно как-то считать стату и трекать клиентов. А юзерам нахер не надо, чтобы их считали, трекали, а потом перепродавали их данные рекламщикам.

Но можно впринципе избежать этой битвы, нужно всего-лишь заюзать, простой советский...

Как работает браузерный фингерпринтинг

В браузер загружается JS скрипт, который собирает базовую инфу: User-Agent (браузер, ОС), Язык (navigator.language), Часовой пояс, Разрешение экрана, Количество CPU (navigator.hardwareConcurrency), RAM (navigator.deviceMemory).

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

Далее через Web Audio API генерится звук, пропускается через эффекты, полученый результат опять отличается для разных сочитаний ОС/железо.

И дополнительно к этому может собираться ещё куча другой инфы.

Пример, что с нас собирают можно посмотреть тут: https://www.amiunique.org/fingerprint

Сочетание всей этой собраной инфы очень разнообразно и позволяет точно идентифицировать юзера без использования кук и авторизации.

Как заблокировать браузерный фингерпринтинг?

Полностью заблокировать браузерный фингерпринтинг можно только отключив javascript. Но современный веб умрёт без js.

Можно через браузерные дополнения(adblock, ghostery) резать CDN поставщиков js кода (fingerprint.com), но это не спасёт, если код будет забандлен в само приложение.

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

Отдельно нужно сказать про brave и tor браузеры которые специализируются на анти-трекинге. В них рандомизация fingerprint данных зашита "Искаропки".

Хорошая новость для рекламщиков: brave/tor не сильно распространён, а CanvasBlocker установлен у исчезающе малого количества человек (~40000 загрузок против ~15000000 загрузок для всяких adблокеров).

Но всётаки по глобальной статистике около 40% юзеров используют какие-то виды адблока.

А это значит, что 40% статы и трекинга либо отсутствует, либо искажена.

Неблокируемый и неискажаемый fingerprint

На уровне http сессии нам доступны заголовки, которые передаёт браузер: accept, accept-language, accept-encoding, referer, user-agent.

Но проблема в том, что эти заголовки всё ещё контролирует клиент. А значит любые заголовки он может подменить. Вот например как легко прикинуться файрфоксом:

curl https://auditica.net -H 'User-agent: Firefox'

Далее на уровне tcp/ip сессии нам доступен ip адрес. Его невозможно подделать, но за одним и тем же адресом могут находиться тысячи клиентов, по этому он не может быть уникальным идентификатором.

И последнее и самое интересное, на уровне tls сессии нам доступны флаги и заголовки рукопожатия:

  • tls_cipher: выбраный шифр
  • tls_curve: кривая выбраная для конкретной сессии
  • tls_alpn: тип протокола выбранного для сессии
  • ssl_ciphers: список поддерживаемых шифров (клиентом)
  • ssl_curves: список поддерживаемых кривых (клиентом)

Чисто в теории эту информацию тоже может контролировать клиент, но это уже требует править код конкретной библиотеки используемой для запросов.

Вот пример как отличаются эти параметры для firefox и Chromium:

Firefox tls info

"tls_cipher":"TLS_AES_128_GCM_SHA256",
"tls_curve":"X25519",
"tls_alpn":"h2",
"ssl_ciphers":"TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA",
"ssl_curves":"X25519MLKEM768:X25519:prime256v1:secp384r1:secp521r1:ffdhe2048:ffdhe3072"

Chromium tls info

"tls_cipher":"TLS_AES_128_GCM_SHA256",
"tls_curve":"X25519",
"tls_alpn":"h2",
"ssl_ciphers":"0xcaca:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA:AES256-SHA",
"ssl_curves":"0x0a0a:X25519MLKEM768:X25519:prime256v1:secp384r1"

И вот примеры для curl и wget:

Curl tls info

"tls_cipher":"TLS_AES_256_GCM_SHA384",
"tls_curve":"X25519",
"tls_alpn":"h2",
"ssl_ciphers":"TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
"ssl_curves":"X25519:prime256v1:X448:secp521r1:secp384r1:ffdhe2048:ffdhe3072:ffdhe4096:ffdhe6144:ffdhe8192"

Wget tls info

"tls_cipher":"TLS_AES_256_GCM_SHA384",
"tls_curve":"X25519",
"tls_alpn":"",
"ssl_ciphers":"TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-CCM8:ECDHE-ECDSA-AES256-CCM:DHE-RSA-AES256-CCM8:DHE-RSA-AES256-CCM:ECDHE-ECDSA-ARIA256-GCM-SHA384:ECDHE-ARIA256-GCM-SHA384:DHE-DSS-ARIA256-GCM-SHA384:DHE-RSA-ARIA256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-CCM8:ECDHE-ECDSA-AES128-CCM:DHE-RSA-AES128-CCM8:DHE-RSA-AES128-CCM:ECDHE-ECDSA-ARIA128-GCM-SHA256:ECDHE-ARIA128-GCM-SHA256:DHE-DSS-ARIA128-GCM-SHA256:DHE-RSA-ARIA128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:ECDHE-ECDSA-CAMELLIA256-SHA384:ECDHE-RSA-CAMELLIA256-SHA384:DHE-RSA-CAMELLIA256-SHA256:DHE-DSS-CAMELLIA256-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:ECDHE-ECDSA-CAMELLIA128-SHA256:ECDHE-RSA-CAMELLIA128-SHA256:DHE-RSA-CAMELLIA128-SHA256:DHE-DSS-CAMELLIA128-SHA256:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:AES256-GCM-SHA384:AES256-CCM8:AES256-CCM:ARIA256-GCM-SHA384:AES128-GCM-SHA256:AES128-CCM8:AES128-CCM:ARIA128-GCM-SHA256:AES256-SHA256:CAMELLIA256-SHA256:AES128-SHA256:CAMELLIA128-SHA256:AES256-SHA:CAMELLIA256-SHA:AES128-SHA:CAMELLIA128-SHA:TLS_EMPTY_RENEGOTIATION_INFO_SCSV",
"ssl_curves":"X25519:prime256v1:X448:secp521r1:secp384r1:ffdhe2048:ffdhe3072:ffdhe4096:ffdhe6144:ffdhe8192"

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

И если эту инфу склеить с ip адресом и http заголовками то получается уже достаточно инфы чтобы без cookie и авторизации определять уникальных ползователей.

Как собрать серверный fingerprint

Для этого нам потребуется nginx, который умеет работать с TLS параметрами. Но передавать их сырыми через proxy_set_header идея так себе, т.к они займут много трафика. По этому лучше собирать кусок фингерпринта сразу в самом nginx с помощью nginx-njs модуля: https://nginx.org/en/docs/njs/install.html.

Далее пишим простенький JS скриптик, в котором собираем tls параметры в 1 строку, барём от них sha256 хэш, и обрезаем хэшь до 16 симфолов. 16 символов будет достаточно.

// fingerprint.js
import crypto from 'crypto';

function tls_fp(r) {
  const parts = [
    r.variables.ssl_protocol || '',
    r.variables.ssl_alpn_protocol || '',
    r.variables.ssl_ciphers || '',
    r.variables.ssl_curves || '',
    r.variables.ssl_cipher || ''
  ].join('|');

  const hex = crypto.createHash('sha256').update(parts).digest('hex');
  return hex.slice(0, 16);
}
export default { tls_fp };

Закидываем этот файл в /etc/nginx и подключаем в nginx.conf:

http {
...
  js_import fp from /etc/nginx/fingerprint.js;
  js_set $tls_fp_hash fp.tls_fp;
...
}

После этого нам будет доступна переменная $tls_fp_hash, которую через proxy_set_header можно пробросить на бэкэнд:

  location / {
    proxy_set_header tls-fp $tls_fp_hash;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Proto $scheme;
    add_header X-Frame-Options SAMEORIGIN always;
    proxy_pass http://localhost:3032; # Адрес хост-машины
  }

Ну и уже на бэкэнде получаем его вместе с остальными заголовками:

{
  'tls-fp': '45ca31c3315a5978',
  connection: 'upgrade',
  host: 'auditica.net',
  'x-real-ip': '172.19.0.1',
  'x-forwarded-proto': 'http',
  'user-agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0',
  accept: 'image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5',
  'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
  'accept-encoding': 'gzip, deflate',
  'sec-gpc': '1',
  referer: 'https://auditica.net/',
  priority: 'u=5, i'
}

Поздравляю мы изобрели эрзац версию JA3 / JA3S.

Зачем нужен серверный fingerprint?

Как и браузерный фингерпринт, он нужен для учёта уникальных посетителей. Чтобы точно собирать статистику посещения.

Он нужен, чтобы различать реальные браузеры от роботов, прикидывающихся браузерами.

На основе серверного фингерпринта можно строить более точные рейтлимитеры не опирающиеся только на ip адрес.

Как это влияет на обычны юзеров?

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

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


Сообщество: AWS

Комментариев(0)


Всего: 0 комментариев на 0 страницах

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



Сообщества