Согласованность часового пояса и локали - самый частый признак, который выдают себе сами
Маршрутизация трафика через прокси меняет одно: IP-адрес, который видит сервер. Она не меняет ничего в операционной системе под браузером, а та продолжает сообщать свой часовой пояс, свой язык и своё форматирование локали ровно так, как настроена. Сессия, заявляющая домашний IP в одной стране, у которой часы, язык и форматирование чисел принадлежат другой, - не тонкий сигнал. Это, пожалуй, самая дешёвая проверка из доступных любому сайту, и чтобы её выполнить, скрипт снятия отпечатков не нужен вовсе.
Все места, где течёт часовой пояс
Intl.DateTimeFormat().resolvedOptions().timeZoneвозвращает имя зоны IANA напрямую, напримерAmerica/Los_AngelesилиEurope/Berlin.Date.prototype.getTimezoneOffset()возвращает смещение от UTC в минутах для текущей даты, и оно обязано совпадать с тем, что подразумевает эта зона IANA прямо сейчас, включая то, по какую сторону перехода на летнее время находится текущая дата.Date.prototype.toString()содержит и числовое смещение, и человекочитаемое имя зоны, например строку, заканчивающуюся наGMT+0100 (Central European Standard Time). Скрипт может прочитать это, вообще не трогаяIntl, поэтому переопределение только одного из трёх оставляет два других нетронутыми и противоречащими.
Все места, где течёт локаль
navigator.languageиnavigator.languagesсообщают предпочитаемый язык и упорядоченный список запасных.- HTTP-заголовок
Accept-Languageгенерируется браузером из того же списка языков и отправляется с каждым запросом, включая самый первый, до выполнения любого JavaScript. Инструмент, меняющийnavigator.language, но не заголовок, противоречит сам себе прямо в журнале запросов сервера. Intl.NumberFormatформатирует числа с разделителями, зависящими от локали, поэтому1234567.89в английской локали отрисуется с точкой и запятой, а в большей части континентальной Европы - с поменянными местами запятой и точкой. Страница может отформатировать известное число и сравнить строку с той локалью, о которой ей сказали.Intl.Collatorзадаёт порядок сортировки строк, и локали расходятся в том, куда попадают некоторые буквы с диакритикой или нелатинские символы относительно обычного алфавита. Отсортировать один и тот же короткий список и захешировать порядок - корректная, хотя и редко используемая проверка согласованности.
Геолокация, если она разрешена
navigator.geolocation.getCurrentPosition() требует явного разрешения, поэтому доступна не всегда, но когда пользователь соглашается, возвращаемые координаты обязаны попадать в ту же страну, куда геолоцируется IP-адрес, и в тот же часовой пояс, о котором сообщают часы. Три независимых сигнала, сходящихся вместе, - куда более сильное утверждение, чем любой из них в одиночку, и именно поэтому сайты, получившие разрешение, обычно сверяют его с двумя другими, а не доверяют изолированно.
Проверка, которой вообще не нужен JavaScript
Сервер может поймать несовпадение, никогда не запуская скрипт снятия отпечатков, пользуясь одним лишь собственным журналом запросов. Он смотрит страну исходного IP и типичное смещение UTC в базе geoIP, а затем сравнивает это с любой меткой времени, которую клиент уже прислал по не связанному поводу: записанным локальным временем из баннера согласия на куки, полем локального времени в форме заказа или значением часов клиента в рукопожатии websocket. Если IP геолоцируется в зону со смещением восемь часов от UTC, а присланная клиентом метка подразумевает часы со смещением пять часов, этот разрыв есть в обычных данных журнала, и никакое свойство из скрипта не требуется. Именно поэтому одна лишь смена прокси, при которой операционная система остаётся со своими настройками времени и языка, - такой частый и лёгкий провал.
Чек-лист
Intl.DateTimeFormat().resolvedOptions().timeZoneсовпадает с регионом выходного IP.getTimezoneOffset()совпадает с тем, что подразумевает эта зона IANA на текущую дату.navigator.languageиnavigator.languagesсовпадают с ожидаемой локалью.- Заголовок
Accept-Language, а не только видимое из JS свойство, тоже совпадает. - Вывод
Intl.NumberFormatиIntl.Collatorвыглядит родным для заявленной локали. - Геолокация, если разрешена, согласуется и с IP, и с часами.
Привязка прокси к профилю заставляет часовой пояс, локаль и геолокацию следовать за выходным IP автоматически, что убирает тот шаг, на котором это обычно и ломается: кто-то меняет прокси для сессии и забывает, что вместе с ним ничто другое в стеке не сдвинулось.
Когда бесплатное и простое решение - верное
Если вам нужно один раз посмотреть страницу так, будто вы в другом часовом поясе, панель Sensors в Chrome DevTools уже бесплатно переопределяет геолокацию, а настройки локали и часового пояса вашей операционной системы, изменённые на время сессии, закрывают остальное для разовой ручной проверки. Ни то, ни другое не требует отдельного профиля браузера и вообще никакого прокси. Чего этот подход не решает - так это поддержания множества постоянных личностей, каждая из которых внутренне согласована между перезапусками и неделями спустя, без того чтобы вы каждый раз вручную вспоминали про шесть свойств. Это другая, непрерывная задача, нежели разовый просмотр одной страницы, и стоит честно понять, какая из них у вас, прежде чем браться за что-то тяжёлое.
Проверьте сами
function localeReport() {
return {
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
offsetMinutes: new Date().getTimezoneOffset(),
dateString: new Date().toString(),
language: navigator.language,
languages: navigator.languages,
numberSample: new Intl.NumberFormat().format(1234567.89),
}
}
console.log(localeReport())
Сравните поля timeZone и offsetMinutes с тем, где на самом деле находится ваш выходной IP, например через любой сервис определения IP, и сравните language с заголовком Accept-Language, который отправляет ваш браузер, - его видно во вкладке сети в devtools на любом запросе. Про сторону выходного IP в этой задаче - в статье резидентные и серверные прокси, а более полный список свойств, обязанных сходиться друг с другом помимо часового пояса и локали, - в чек-листе согласованности отпечатка.
Попробуйте на бесплатном тарифе.
Неограниченное число локальных профилей, без карты. Проверьте результат детекторами сами.