Назад в Центр знаний
Отпечатки

Предсказание CreepJS: агрегация и неизвестные состояния

Сопоставьте поиск getPrediction в CreepJS с агрегацией сигналов renderPrediction и unknown-состояниями: ни один вывод не доказывает личность устройства.

BotBrowser Team

Документация

Нужна структурированная документация по теме Отпечатки?

Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.

BotBrowser может поддерживать согласованность заявленного профиля между поверхностями, видимыми браузеру, но не может подтверждать корректность стороннего предсказания по общей выборке. В закреплённом коде CreepJS getPrediction() ищет строку общих данных и выбирает кандидатов, а renderPrediction() отдельно читает decryptionData, объединяет поля сигналов и показывает состояния проверки или оценки. Это разные пути кода; ни один результат не доказывает личность человека или машины и не выносит вердикт о боте.

TL;DR

  • getPrediction ищет в переданных данных строку, у которой id совпадает с запрошенным хешем; сравнение также допускает преобразование обоих значений в строки. Локальная переменная decrypted хранит ключ найденного объекта; эта функция не расшифровывает строку.
  • Поля системы, GPU и бренда GPU возвращаются только тогда, когда соответствующие списки кандидатов содержат ровно одно значение. Для устройства дополнительно может вернуться общая базовая часть нескольких имён.
  • Рендерер сводит значения из 18 именованных записей сборщиков к трём объектам JavaScript Set: для бренда GPU, GPU и устройства. Set удаляет повторяющиеся значения, после чего код удаляет undefined; агрегация не сохраняет сведения о том, какой сборщик дал каждое оставшееся значение.
  • unknownHTML, pendingReview, оценки смешения данных, маркеры предсказания и маркер ошибки относятся к разным ветвям. Ни одна из них не является откалиброванной вероятностью правильности метки.

Contents

Два пути CreepJS: поиск хеша и кандидаты в getPrediction; агрегация сигналов и состояния в renderPrediction
В закреплённой версии поиск, сокращение кандидатов, агрегация и отображение интерфейса разделены.

Метка предсказания является результатом агрегации

Слово «предсказание» может подтолкнуть к более сильной трактовке, чем допускает реализация. Увидев рядом имя устройства и оценку, читатель может решить, что страница распознала физическое устройство или измерила вероятность правильности имени. Закреплённая реализация CreepJS не подтверждает ни одного из этих выводов. Она показывает, как из входных данных выбираются и отображаются отдельные значения; в исходнике нет исследования калибровки, доверительного интервала или идентификатора конкретного человека.

Две функции отвечают на разные вопросы. getPrediction ищет одну строку общих данных и сокращает четыре списка кандидатов до полей system, device, gpu и gpuBrand. renderPrediction получает объекты сборщиков, объединяет выбранные поля между ними, а затем формирует разметку области предсказания и отдельных строк сигналов. Результат поиска и отрисованную сводку сборщиков не следует сводить к одной непрозрачной оценке.

В руководстве CreepJS по группам сигналов объясняется чтение меток по поверхностям, а анализ перекрёстной проверки групп прослеживает упорядоченные шаблоны за botHash. Эти темы не разбирают сопоставление строки общих данных и ветви кандидатов и отображения панели предсказания.

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

Область разбора ограничена закреплённой ревизией 10aa6724cd33a1015db1574211890518cd04f0cc файла CreepJS src/prediction/index.ts, а не текущим наблюдением за работающим сервисом CreepJS. Общий материал /docs/proof/fingerprinting-privacy-threat/ описывает сигналы отпечатка браузера, но не картирует ветви этого модуля предсказания.

Сначала найдите строку общих данных

getPrediction получает hash и data. Функция перебирает ключи data, ищет в массиве под каждым ключом и сравнивает id каждого элемента с запрошенным хешем. Сравнение допускает как прямое равенство, так и равенство после преобразования обоих значений в строки. Ключ, содержащий совпавший элемент, сохраняется в локальной переменной с именем decrypted.

Это имя переменной не доказывает выполнение криптографической операции. В этой функции важны поиск в массиве и сравнение идентификаторов. Исходный код здесь не расшифровывает элемент. Точное описание операции: «поиск строки общих данных по хешу», а не «расшифровка предсказания». Имя локальной переменной не меняет операцию, которую выполняет код.

После нахождения совпавшего элемента функция копирует в локальные переменные четыре массива: devices, systems, gpus и gpuBrands. Если массива нет, функция использует пустой массив. Затем объект результата выбирает публичное значение из каждого списка. Поэтому поиск может завершиться успешно, хотя одно или несколько возвращаемых полей останутся undefined: найти строку и получить одно однозначное значение это разные этапы.

Исходное значениеДействие поискаОсторожная трактовка
item.idСравнить с переданным hash, в том числе после преобразования в строкиВ переданном объекте данных найдена совпавшая запись.
item.systemsИспользовать пустой список при отсутствии; вернуть систему только для одного кандидатаНоль или несколько кандидатов не дают единственного значения system.
item.gpusИспользовать пустой список при отсутствии; вернуть GPU только для одного кандидатаОтсутствующая метка GPU не означает, что у браузера нет GPU.
item.gpuBrandsИспользовать пустой список при отсутствии; вернуть бренд только для одного кандидатаФункция не ранжирует несколько брендов и не задаёт уровень уверенности для одного из них.
item.devicesИспользовать пустой список при отсутствии; вернуть кандидата или общую базовую частьОбщий текстовый префикс служит правилом выбора, а не независимой проверкой оборудования.

Последнюю строку особенно легко истолковать слишком сильно. В комментарии к коду Android 10 приведён как базовое имя, общее для Android 10 и Android 10 Blah Blah. Это пример правила включения одной строки в другую. Он не показывает, что CreepJS подтвердил точную версию ОС в работающем браузере.

Число кандидатов определяет возвращаемые значения

Правила возврата намеренно асимметричны. Для трёх полей требуется ровно один кандидат, а у device есть запасной вариант для общей базовой части. Длина массива объясняет, почему видимое поле может отсутствовать даже при наличии строки общих данных.

ПолеУсловие кандидата в закреплённом кодеВозвращаемое значение при невыполнении условия
systemsystems.length == 1undefined
gpugpus.length == 1undefined
gpuBrandgpuBrands.length == 1undefined
deviceОдно устройство или базовое имя, входящее в каждую строку-кандидатПомощник общей базы возвращает undefined, если ни одно имя не общее для всех.

Для систем, моделей GPU и брендов GPU реализация не выбирает самый частый вариант и не сортирует кандидатов по оценке. Она проверяет, состоит ли массив из одного элемента. Если нет, поле становится undefined. Это явная граница неоднозначности, а не скрытое ранжирование.

Для устройств getBaseDeviceName ищет строку-кандидат a, которая содержится во всех строках-кандидатах b. При одном кандидате код возвращает его сразу, не вызывая помощник. При нескольких кандидатах может вернуться общая базовая часть; если ни одно имя не входит во все строки, эта ветвь не возвращает одно имя устройства. Правило основано на тексте, его нельзя описывать как вычисление расстояния между аппаратными конфигурациями.

Следующие примеры по исходнику показывают путь кода, а не текущий отчёт:

Список кандидатовРезультат пути кодаЧего результат не доказывает
В systems одна записьОна возвращается как systemЧто метка идентифицирует ОС конкретного человека.
В gpus две записиЕдинственное значение gpu не возвращаетсяЧто оба GPU отсутствовали у всех сборщиков.
devices содержит Android 10 и Android 10 Blah BlahМожет вернуться общая база Android 10Что конкретная версия Android независимо проверена.
devices содержит несвязанные имена без общей для всех базыПомощник базы не возвращает значениеЧто браузер недействителен или автоматизирован.

Значение undefined полезно сохранять как отдельное состояние. Замена его первым кандидатом, предполагаемым большинством или выведенным устройством добавила бы правило выбора, которого в этой функции нет. В таблице сравнения следует отдельно записывать «единственное значение не возвращено» и «исходное поле отсутствовало».

Восемнадцать записей питают три множества без дублей

Агрегированные поля gpuSystem и gpuModelSystem не описывают создание WebGL-контекстов или поддержку расширений; разбор WebGL-отчёта CreepJS отдельно рассматривает сигналы контекста и возможностей рендерера.

renderPrediction читает именованные объекты сигналов и создаёт три экземпляра Set. Каждое множество получает свойство из 18 записей: jsRuntime, emojiSystem, domRectSystem, svgSystem, mimeTypesSystem, audioSystem, canvasSystem, canvasPaintSystem, canvasTextSystem, canvasEmojiSystem, textMetricsSystem, webglSystem, gpuSystem, gpuModelSystem, fontsSystem, voicesSystem, screenSystem и deviceOfTimezone.

Свойства проецируются отдельно. Одно множество собирает gpuBrand, другое собирает gpu, третье собирает device. Перед чтением свойства для каждой записи используется резервный пустой объект. После построения каждого множества код удаляет undefined. Одинаковые повторяющиеся значения схлопываются до одного элемента, поскольку Set хранит только уникальные значения.

Агрегированное множествоСвойство из каждой из 18 записейПрямое преобразование в исходнике
gpuBrands.gpuBrandСоздать множество, затем удалить undefined.
gpus.gpuСоздать множество, затем удалить undefined.
devices.deviceСоздать множество, затем удалить undefined.

Операция сводит коллекцию именованных полей к уникальным значениям, но итоговое множество не показывает, какой сборщик дал каждое оставшееся значение. Если пять сборщиков возвращают один gpuBrand, в множестве будет одно значение, а не счётчик пять. Если два сборщика расходятся, множество может содержать два значения, но интерфейс всё равно не сообщает, какой API дал каждое из них. Исходный модуль показывает список источников в коде; если происхождение важно для сравнения, его нужно сохранять отдельно.

Множество также не равно списку кандидатов строки общих данных. Массивы в getPrediction отвечают на вопрос, можно ли вернуть одно значение для хеша. Множества сборщиков в renderPrediction объединяют значения объектов сигналов для видимой сводки. Одно слово вроде «консенсус» для обоих путей скрыло бы это различие.

18 исходных полей можно сгруппировать по названной поверхности, но эти названия не означают одинаковое качество или независимость. Canvas image, Canvas paint, Canvas text и Canvas emoji представлены отдельными входами в коде. То же верно для gpuSystem и gpuModelSystem. Считать их полностью независимыми голосами означало бы добавить статистическое допущение, которого нет в реализации.

Разделяйте выбор устройства и состояния оценки

Перед отображением имени устройства интерфейс вычисляет специальный выбор Windows Remote Firefox Privacy. Код подсчитывает строки устройств, содержащие «Windows», и делит число таких строк на общее число строк. Эта ветвь работает только при доле выше 0.5; затем она проверяет фиксированный порядок предпочтений: Windows 7, Windows 8, Windows 8.1, Windows 11 и Windows 10, сначала проверяя написание с 64 битами для каждой версии. Если доля не превышает половину, помощник возвращает undefined.

Итоговый deviceName берётся из помощника RFP или, если результат falsy, из общей базовой части имён устройств. Комментарий говорит, что в данных выборки Firefox RFP игнорируется, поскольку возвращает Windows 10, а затем объясняет порядок предпочтений при наличии нескольких версий Windows. Это правило отображения конкретного источника. Оно не доказывает, что вся популяция следует такому порядку, и не должно подаваться как в целом точный определитель ОС.

crowdBlendingScoreGrade сопоставляет числовые диапазоны с буквами: A от 90, B от 80, C от 70, D от 60 и F ниже 60. Разметка показывает процент и букву только если тип crowdBlendingScore в JavaScript равен number. Отдельная ветвь bot показывает locked. Код задаёт отображение оценок и условие интерфейса, но не устанавливает, что предсказывает оценка и насколько она откалибрована.

Модуль показывает pendingReview в отдельной заметке, когда поле truthy. Эту метку нужно записывать отдельно от оценки и имени предсказания. Закреплённый исходник не позволяет заменить её словами «ошибка», «одобрено» или «проверка человеком завершена» без другого источника, который определяет это состояние.

Несколько деталей интерфейса можно записывать, не превращая их в заявления об уверенности:

Видимое состояниеУсловие исходникаОграниченная трактовка
** перед объединённой меткойdeviceName и gpuBrandName truthyРендерер располагает обеими строками. Звёзды не являются вероятностью.
* перед одной меткойTruthy только одна из двух строкВ этой ветви доступна одна из двух строк отображения.
Пустые значки предсказанияОбе строки falsyЭта ветвь не показывает объединённую метку.
pending review: …pendingReview truthyОтображается переданное поле; закреплённый исходник не определяет итог проверки.
Оценка и буква смешения данныхЗначение имеет тип number, а ветвь блокировки не сработалаИнтерфейс показывает число и диапазон оценки; точность неизвестна.
lockedВетвь bot truthyИнтерфейс использует ветвь блокировки и не показывает в ней оценку.

Помощник hasValue требует отдельной осторожности. Он ищет первое значение свойства, тип которого не undefined; вызывающие его ветви отрицают результат, чтобы выбрать unknownHTML. Определённое значение false или 0 само является falsy и поэтому может попасть в ветвь неизвестного состояния. Следовательно, метка unknown не доказывает, что сборщик не запускался. Она может отражать то, как помощник обрабатывает falsy-значение. Это наблюдение ограничено закреплённой реализацией и требует повторной проверки, если в более поздней версии помощник изменится.

predictionErrorPatch образует ещё одну ветвь. Если передать ошибку и элемент предсказания существует, функция показывает Prediction Failed и пустые значки во многих строках. Этот маркер описывает интерфейс ошибки, а не правило числа кандидатов и не успешный запуск, в котором отсутствовали все сигналы. В записи нужно отделять ветвь ошибки от обычного отчёта, где неизвестными отображаются отдельные строки.

Сохраняйте запись сравнения с привязкой к источнику

Если два отчёта отличаются, сначала запишите вопрос и ревизию источника, а уже затем трактуйте метки. Эти поля сохраняют различия реализации без копирования необработанного хеша и полного идентификатора браузера:

Поле записиЧто сохранитьПочему это важно
ИсточникРепозиторий, SHA коммита и путь файлаПоздняя ревизия может изменить поиск или отображение.
Состояние поискаБыла ли в данных строка с совпавшим хешем; сам хеш не сохранятьНаличие строки и уникальность поля: отдельные результаты.
Число кандидатовЧисло вариантов для систем, устройств, GPU и брендов GPUПравила возврата зависят от длины массива и общей базы устройства.
Состояние агрегацииКакие именованные поля сборщиков присутствовали и имело ли множество ноль, одно или несколько уникальных значенийДедуплицированное множество не хранит исходный список вкладов.
Состояние отображенияМаркер предсказания, ветвь unknown, pendingReview, показ числовой оценки, блокировка или маркер ошибкиПохожие пустые области могут появляться по разным путям.
ОграниченияРазбор закреплённого кода, текущее наблюдение страницы или явно ограниченный другой источникОдин лишь код не доказывает текущий результат сервиса или его точность.

Не сохраняйте переданный хеш только потому, что поиск использует его. Для объяснения пути обычно достаточно логического признака наличия совпавшей строки и числа кандидатов. Сохранение необработанного идентификатора увеличивает объём данных без улучшения объяснения ветви.

Используйте эту таблицу для трактовки и сравнения поведения открытых источников. Это не протокол воспроизведения серверной части CreepJS, не тест её точности и не способ изменить оценку стороннего сервиса. Для таких утверждений нужны другие доказательства, которых в этом исходном файле нет.

Sources

Practical conclusion

Читайте панель предсказания CreepJS как набор зависящих от источника выходных состояний. Сначала проверьте наличие совпавшей строки общих данных, затем отдельно сохраните числа кандидатов, уникальные значения сборщиков и состояние отображения. Возвращённое одиночное значение, общая база имени устройства, диапазон оценки, заметка о проверке и неизвестный маркер не являются взаимозаменяемыми доказательствами. Закреплённый код объясняет, как собираются метки; он не превращает их в подтверждённую идентичность устройства или откалиброванный вердикт о боте.

#CreepJS#Browser Fingerprinting#приватность

Эта статья входит в руководство: Что такое отпечаток браузера?

Переведите BotBrowser из исследований в продакшн

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