Intl API открывает базу локалей ICU внутри браузера. Какие вызовы Intl дают отпечаток, почему они выдают версию сборки и почему их трудно подделать.
Спросите большинство людей, какая часть JavaScript-API Intl используется для фингерпринтинга, и они назовут часовой пояс. Он действительно самый известный, и ему посвящена статья Утечки часового пояса и языка. Но Intl — это не один сигнал. Это окно в базу данных локалей, с которой поставляется ваш браузер, а такая база велика, имеет версии и различается между движками, платформами и сборками. Вопросы, которые к ней можно задать, несут куда больше информации, чем одна строка с часовым поясом.
Главное
Intlраскрывает данные локалей типа ICU, встроенные в браузер или операционную систему, поэтому ответы зависят от движка, ОС и версии сборки, а не только от выбранного языка.- Три значения локали могут расходиться:
navigator.languages(заявленное), заголовокAccept-Language(отправленное) иIntl.DateTimeFormat().resolvedOptions().locale(согласованное средой выполнения). Само расхождение — это сигнал. Intl.supportedValuesOf()возвращает длинные списки календарей, правил сортировки, валют, систем счисления, часовых поясов и единиц. Отпечаток — весь список, и он же датирует вашу сборку.- Вывод
Intl.NumberFormat,Intl.DateTimeFormatиIntl.Collatorможет отличаться между версиями данных, а в браузерах, учитывающих региональные настройки ОС, умолчания могут выдать регион, даже если язык интерфейса английский. - Поскольку данные зашиты в бинарный файл, подделать их согласованно трудно. Tor Browser и
privacy.resistFingerprintingв Firefox могут нормализовать их доen-USценой удобства.
Часовой пояс: один абзац — и дальше
Да, Intl.DateTimeFormat().resolvedOptions().timeZone — самая известная утечка Intl, а несовпадение со страной IP — классический признак VPN. Повторяться не будем: этой теме посвящена отдельная статья Утечки часового пояса и языка. Дальше речь о всей остальной поверхности, о которой говорят гораздо меньше.
Три значения локали — три источника
У браузера есть несколько понятий «ваш язык», и берутся они из разных мест:
navigator.languages— упорядоченный список, который браузер сообщает скриптам.Accept-Language— то, что браузер реально отправляет с каждым запросом.resolvedOptions().localeуIntl.DateTimeFormatи его собратьев — то, что среда выполнения согласовала, сопоставив ваш запрос с данными локалей, которые у неё действительно есть.
В нетронутом браузере эти значения соотносятся предсказуемо. Если инструмент правит первые два и забывает третье, страница видит заявленный профиль, который не подтверждает сама среда выполнения. Это та же логика, что у согласованности отпечатка и обнаружения подмены User-Agent: уличает не отдельное значение, а противоречие.
Самый энтропийный элемент: Intl.supportedValuesOf()
Intl.supportedValuesOf(key) принимает один из ключей calendar, collation, currency, numberingSystem, timeZone или unit и возвращает отсортированный список поддерживаемых значений. Списки длинные и следуют за версией данных локалей, встроенных в сборку.
Важно, что отпечаток — это весь список, а не какая-либо одна запись. Новая валюта, переименованный или вновь распознанный часовой пояс, дополнительная единица — всё это меняет список. Поскольку версии данных обновляются вместе с выпусками браузеров и ОС, список служит ещё и датой сборки: он помещает вас в окно релизов, даже не читая строку User-Agent. Хеш из шести списков — компактный идентификатор, стабильный, пока стабильны данные браузера, и общий для всех, у кого те же данные.
Вывод форматирования как зонд
Другой подход — перестать перечислять и начать форматировать. Подайте среде выполнения фиксированный ввод и сравните результат:
Intl.NumberFormatопределяет разделители групп, десятичный знак, положение символа валюты и компактную запись.Intl.DateTimeFormat.formatToParts()возвращает дату размеченными частями, поэтому видны порядок частей и литеральные строки между ними.Intl.Collatorопределяет, как сортируются строки с диакритикой, смешанным регистром и CJK.
Разные версии данных и разные платформенные данные могут давать заметно разный вывод на один и тот же ввод. Мы намеренно не приводим точные строки для конкретных версий: они меняются, и мы их не проверяли; важен сам класс различий. Трекеру не нужно знать правильный ответ — достаточно стабильного хеша от многих ответов.
Умолчания, привязанные к региону ОС, а не к языку
В зависимости от браузера и платформы часть умолчаний Intl следует региональным настройкам операционной системы, а не языку, выбранному в браузере. Самый наглядный случай — Safari на устройствах Apple; Chrome и Firefox в основном берут умолчания из собственной языковой настройки браузера, и это само по себе различие между движками. Затронуты могут быть:
- календарь по умолчанию (григорианский, буддийский, японский императорский и т. д.);
- система счисления по умолчанию (латинские цифры, арабо-индийские, деванагари);
- формат часов (12- или 24-часовой) и первый день недели.
Человек может читать английские страницы в браузере с английским интерфейсом, а ОС у него настроена на Таиланд или Саудовскую Аравию. Там, где браузер учитывает эту настройку, navigator.language скажет «английский», а resolvedOptions() тихо выдаст регион. Это отдельный сигнал, не совпадающий с заголовком языка, и он помогает прикинуть, где устройство было настроено.
Почему согласованно подделать это трудно
Антидетект-браузер может переписать User-Agent, navigator.languages и строку часового пояса, потому что это значения на JavaScript-объектах. Сами данные локалей зашиты в бинарный файл или ОС. Чтобы остаться согласованным, слою подмены пришлось бы перехватить каждый конструктор Intl, каждый ключ supportedValuesOf, каждый вызов formatToParts, а также toLocaleString у чисел, дат и строк — и везде отвечать в согласии с заявленной платформой и версией. Упустите одно место — и заявленный профиль противоречит среде выполнения. Как такие пробелы находят на практике, см. в статье антидетект-браузеры против настоящих.
Мы описываем сигналы, чтобы вы могли их понимать и проверять, а не как рецепт подделки.
Защита и её цена
Tor Browser и Firefox с включённым privacy.resistFingerprinting закрывают эту поверхность нормализацией: часовой пояс сообщается как UTC, а если согласиться на предложение запрашивать английские версии страниц, всем сайтам сообщается и одна локаль — en-US. Все в этой толпе выглядят одинаково, в этом и смысл.
Цена вполне реальна: даты, числа и порядок сортировки форматируются для чужой локали, а сайт, доверяющий сообщённому значению, покажет пользователю из Германии или Японии неверные соглашения. Нормализация покупает анонимность ценой удобства, поэтому большинство браузеров не включает её по умолчанию.
Насколько велик сигнал?
Если оценивать честно, Intl даёт энтропию средней величины, не уровня canvas. Распространённая локаль вроде en-US в актуальном Chrome разделяет свой профиль Intl с огромным числом людей. Сигнал информативнее для редких локалей, смешанных регионов ОС и браузера, старых и самосборных версий, а ценнее всего он как перекрёстная проверка других сигналов. Как об этом думать, см. в статье об энтропии и множествах анонимности. Посмотреть, что сообщает ваш браузер, можно в проверке отпечатка — она работает целиком в браузере и ничего не отправляет на сервер.


