Правила сетевого уровня, недоступные странице
Публичный набор делит 126 правил: 95 проверяет страница, а 31 требуют сети. Реальные рукопожатия показывают, где живут самые тяжёлые правила.
BotBrowser Team
Нужна структурированная документация по теме Отпечатки?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
BotBrowser сохраняет согласованность объявленного профиля на поверхностях, которые страница может прочитать, и ведёт ограниченную запись того, что страница сообщила о своём окружении. Он не предоставляет рукопожатие TLS, кадра настроек HTTP/2 или адреса, который прокси показывает на дальнем конце соединения. Публичный набор правил читается как данные: правила делятся по уровню, который нужен каждому, а сетевые факты измеряются на реальных рукопожатиях, так что деление опирается на наблюдаемые байты, а не на предположение о том, что видит сеть.
Кратко
- В наборе 126 правил. Страница может вычислить 95 из них; остальные 31 требуют факта, который до страницы не доходит.
- Все 31 сетевых правила читают хотя бы одно значение вне страницы, а 25 из них не читают ничего другого.
- Это подмножество состоит из 23 правил согласованности, 4 правил репутации, 3 правил автоматизации и 1 правила артефакта; по уровням они распределены так: сеть 28 раз, браузер 14 раз, репутация 4 раза.
- Только 6 из 31 сравнивают значение страницы со значением сети, и именно их страница может закрыть наполовину: одну сторону она даёт, для другой у неё ничего нет.
- Любое сетевое правило весит не меньше 0.5. Четыре стоят на 0.85 и выше, 8 между 0.7 и 0.84, 19 между 0.5 и 0.69.
- Три самых тяжёлых правила имеют вес 0.9, и все три описывают поведение кадров HTTP/2, которое не может прочитать ни один скрипт страницы.
- Два рукопожатия, различающиеся только списком протоколов, дают
alpn=h2иalpn=http/1.1при одной версии TLS 1.3, одном шифре и одной группе обмена ключами. - Кадр настроек с начальным окном 1048576 и максимальным кадром 32768 приходит как запись в 18 байт, которую партнёр читает напрямую; страница, отправившая его, не может прочитать ни одного из этих чисел.
- Серверная сторона реального соединения сообщила начальное окно 65536, максимальный кадр 16777215 и 256 одновременных потоков, и всё это видно сетевому стеку клиента и ни одному скрипту на странице.
- Отчёт со стороны страницы может описать окружение, которое она выставляет, и не может опровергнуть правило, входов которого она не видит.
Содержание
- Где набор правил делится
- Какие шесть правил закрываются наполовину
- Что реальное рукопожатие отправляет в сеть
- Почему самые тяжёлые правила остаются на другой стороне
- Чем чтение сетевой находки отличается
- Записать чтение
Где набор правил делится
Набор правил это список утверждений, и каждое утверждение называет значения, которые читает. Важно здесь не категория и не вес, а то, где эти значения живут. Проба читает опубликованный набор как данные и сортирует каждое правило по тому, может ли скрипт внутри страницы прочитать те же входы:
node=v26.9.0
ruleset=0.74.49 rules=126 digest=cf896731d6c894fb38266206bc121db1f235f536ffbf334aa2c0da7468cc0e59
split -> page_evaluable=95 edge_only=31
edge_categories -> coherence=23 reputation=4 automation=3 artifact=1
edge_layers -> network=28 browser=14 reputation=4
edge_rules_with_a_wire_only_read -> 31 of 31
every_edge_rule_needs_the_wire -> true
edge_rules_with_no_page_read_at_all -> 25
Первая строка деления решает, какую часть детектора страница способна осмыслить. Девяносто пять правил читают значения, которые есть в документе: строку агента пользователя, список языков, геометрию экрана, часовой пояс, форму нативной функции. Страница, которая хочет знать, что скажут эти правила, может прочитать те же значения и получить тот же ответ.
Остальные 31 читают другое. Суммы по уровням больше 31, потому что правило может охватывать несколько уровней, а счёт по категориям показывает, где они собираются: согласованность с 23 далеко впереди, что соответствует названию, ведь правило согласованности существует, чтобы сравнивать два источника, которые должны совпадать. Репутация с 4 спрашивает, принадлежит ли адрес или сеть диапазону хостинга либо известному выходу прокси. Автоматизация с 3 и артефакт с 1 завершают набор.
Строка, на которой держится аргумент, это every_edge_rule_needs_the_wire -> true. Каждое из 31 правил называет хотя бы один вход, который не является значением страницы, поэтому ни одно нельзя решить изнутри документа. Следующая строка сужает ещё сильнее: 25 из 31 читают только сетевые значения. Для них страница не частичный свидетель, она вообще не свидетель. Нет запроса на стороне страницы, который вернёт то же число, нет свойства, отсутствие которого заменит пропущенное значение, и нет арифметики, которая его восстановит.
Это различие разделяет два типа отчётов, похожих снаружи. Проверка на стороне страницы даёт список находок о поверхностях, до которых она дотянулась, а проверка на стороне сети даёт находки о соединении. Когда оба сливаются в одну оценку, слияние скрывает, какая половина дала какую находку, и читатель не может понять, чистый результат означает согласованность страницы или то, что соединение вообще не проверяли. Согласованность сетевого слоя и браузера называет слои, которые раскрывает сессия, а отпечаток сетевой информации показывает, до каких из них страница добирается сама. Публичный каталог наборов показывает тот же разрыв снаружи: что на самом деле проверяют публичные наборы тестов считает, сколько их опубликованных тестов называют соединение.
Какие шесть правил закрываются наполовину
Шесть из 31 сопоставляют значение страницы со значением сети. Это самые интересные случаи: страница даёт половину сравнения и не имеет ничего для другой половины.
| Вес | Сторона страницы | Сторона сети |
|---|---|---|
| 0.85 | browser.webrtc_public_ip | network.observed_ip |
| 0.7 | browser.ua_browser | network.ja4_browser_hint |
| 0.6 | browser.ua_browser | network.h2_browser_hint |
| 0.6 | browser.ua_platform | network.ch_platform_header |
| 0.6 | browser.ua_browser | network.ch_ua_browser |
| 0.55 | browser.nav_language_primary | network.accept_language_primary |
Проба печатает форму самого тяжёлого из них напрямую:
cross_layer -> weight=0.85 net.webrtc_ip_vs_observed page=browser.webrtc_public_ip wire=network.observed_ip
page_side_view -> net.webrtc_ip_vs_observed page_reads=1 wire_reads=1
page_side_view -> predicate=not_equal needs_both_sides=true
page_side_view -> a page can supply browser.webrtc_public_ip and nothing for network.observed_ip
Предикат здесь not_equal, и именно это делает пару трудной для страницы. Страница может прочитать адрес, который сообщает её собственный сбор ICE, а реализация WebRTC, возвращающая публичный адрес, возвращает значение, которое страница может сравнить с тем, что знает сама. Чего страница прочитать не может, так это адреса, который принимающая сторона наблюдала для того же соединения, потому что это значение существует только на дальнем конце. Страница, которая сравнивает свой адрес WebRTC со своим же представлением о своём адресе, сравнивает одно локальное значение с другим локальным значением, и это не то правило.
Та же структура у остальных пяти. Страница читает свой агент пользователя и не может прочитать семейство браузера, которое подразумевало рукопожатие TLS. Страница читает navigator.language и не может прочитать основной язык заголовка Accept-Language, отправленного сетевым стеком. Страница читает строку платформы и не может прочитать заголовок платформы из client hints. В каждом случае значение страницы реально и значение сети реально, и только одно из них достижимо из скрипта.
Есть и второе следствие, которое легко пропустить. Поскольку правилу нужны обе стороны, страница не может даже узнать, сработало бы правило. Она вычисляет свою половину и останавливается. Развёртывание, которое меняет поверхность страницы под целевой профиль, изменило один вход сравнения с двумя входами, и совпадает ли сравнение теперь, зависит от факта, который из страницы не запросить.
Что реальное рукопожатие отправляет в сеть
Утверждение, что эти значения существуют вне страницы, легко произнести, и его стоит измерить. Проба открывает два соединения TLS к одному хосту, различающиеся только списком протоколов, который предлагает клиент, и читает, что соединение согласовало:
handshake:dual_alpn -> alpn=h2 protocol=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 group=X25519MLKEM768
handshake:http11_only -> alpn=http/1.1 protocol=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 group=X25519MLKEM768
wire_only_distinction -> alpn_differs=true
Оба соединения доходят до TLS 1.3, оба соглашаются на TLS_AES_256_GCM_SHA384 и оба используют группу обмена X25519MLKEM768. Отличается только согласованный протокол, h2 против http/1.1, и это различие пришло из параметра клиента, который партнёр прочитал во время рукопожатия. Ничто в документе этого не наблюдает. Страница, загруженная по любому из двух соединений, видит те же API, те же заголовки на своих запросах после отправки и то же окружение; протокол, который перенёс запрос, это свойство соединения, а не документа.
То же верно для кадра настроек HTTP/2. Проба собирает два кадра настроек и читает их обратно из сырых байт на локальном приёмнике, поэтому числа берутся из кадра, а не из объекта клиента:
settings_frame:wide -> enable_push=0 initial_window=1048576 max_frame=32768 bytes=18
settings_frame:narrow -> enable_push=1 initial_window=65535 max_frame=16384 bytes=18
settings_frame_differs -> initial_window_differs=true max_frame_differs=true
Оба кадра занимают 18 байт. Они несут начальное окно 1048576 против 65535, максимальный кадр 32768 против 16384 и противоположные настройки отправки. Партнёр, который хочет знать, как ведёт себя клиент, читает эти записи по шесть байт по мере прихода, ещё до появления документа. Страница не может прочитать отправленный кадр, потому что кадр пишет сетевой стек ниже документа и наверх его не отдаёт.
Серверная сторона реального соединения тоже читаема сетевым стеком клиента:
peer_settings:botbrowser.io -> initial_window=65536 max_frame=16777215 max_streams=256 header_table=4096
Начальное окно 65536, максимальный кадр 16777215, 256 одновременных потоков и таблица заголовков 4096 это значения, которые клиент получил от сервера. Страница может отправлять запросы и наблюдать ответы, и не может прочитать ни одно из этих четырёх чисел, потому что они описывают транспорт, по которому идут запросы.
Вместе измерения устанавливают форму разрыва, а не конкретное обнаружение. Соединение несёт список протоколов, набор шифров, группу обмена ключами, кадр настроек и наблюдаемый адрес. Документ несёт агент пользователя, язык, платформу и набор отрисованных поверхностей. Эти наборы пересекаются ровно в шести местах из таблицы выше, и в каждом сравнение требует значения с каждой стороны.
Почему самые тяжёлые правила остаются на другой стороне
Если бы сетевые правила были тонким хвостом слабых проверок, проверки со стороны страницы хватило бы для темы. Веса говорят обратное:
edge_weight_bands -> ge_0.85=4 0.7_to_0.84=8 0.5_to_0.69=19 lt_0.5=0
heaviest -> weight=0.9 net.h2_rapid_reset layers=network
heaviest -> weight=0.9 net.h2_continuation_flood layers=network
heaviest -> weight=0.9 net.h2_control_flood layers=network
heaviest -> weight=0.85 net.webrtc_ip_vs_observed layers=network+browser
heaviest -> weight=0.8 net.ja4_unstable_within_session layers=network
single_wire_fact_rules -> 21
Ни одно сетевое правило не весит меньше 0.5. Распределение сосредоточено в середине и вверху: 19 правил между 0.5 и 0.69, 8 между 0.7 и 0.84 и 4 на 0.85 и выше. Три самых тяжёлых имеют вес 0.9 и все три описывают поведение кадров в соединении HTTP/2. Одно про сброс потока, который приходит быстрее, чем клиент мог бы обработать ответ. Другое про поток кадров продолжения. Третье про поток управляющих кадров. Каждое это свойство времени или объёма транспорта, и каждое измеряется подсчётом кадров на принимающей стороне.
Два оставшихся тяжёлых правила описывают стабильность, а не одно значение. net.ja4_unstable_within_session с весом 0.8 спрашивает, менялись ли параметры рукопожатия клиента внутри сессии, а net.webrtc_ip_vs_observed с весом 0.85 спрашивает, расходятся ли два адреса одного клиента. Обоим нужно больше одного наблюдения во времени или между уровнями, и это вторая причина, по которой страница их не закроет: даже если скрипт смог бы прочитать один параметр рукопожатия, доступа к предыдущему у него нет.
Двадцать одно из 31 проще. Это проверки наличия по одному сетевому факту: не отсутствовал ли заголовок, который клиент отправил, не пропустил ли принятый набор кодировок метод сжатия, нёс ли рукопожатие параметр, который текущий клиент обычно включает. Один факт решает правило, и факт целиком живёт вне документа. Это сочетание, один вход и вес не меньше 0.5, и делает сетевое подмножество настоящей частью детектора, а не сноской.
Практический вывод про покрытие, а не про оценки. Разбор со стороны страницы может описать 95 из 126 правил и ни одного из 31, которые читают соединение. Когда отчёт перечисляет находки и не говорит, какой уровень их дал, отсутствующие 31 невидимы: отчёт не ошибается в том, что видел, и молчит об уровне, где четыре правила весят 0.85 и больше.
Чем чтение сетевой находки отличается
Находка со стороны страницы и сетевая находка подтверждают разные утверждения, и это различие решает, что должен содержать отчёт.
Находка со стороны страницы воспроизводима по одному документу: любой, кто загрузит ту же страницу в том же окружении, прочитает те же значения и придёт к тому же выводу, а расхождение двух запусков указывает на изменение окружения. Сетевая находка воспроизводима только вместе с соединением: тот же документ, загруженный по другому списку протоколов, с другим шифром или по другому маршруту, даёт другой вход, и два расходящихся запуска могут отличаться транспортом, а не браузером.
Поэтому список полей записи для сетевой находки длиннее. Нужно назвать соединение, из которого пришло наблюдение, а не только страницу: в запусках выше это согласованный alpn=h2 или alpn=http/1.1, а не адрес страницы. Нужно указать, какая сторона каждого сравнения измерена, потому что правило net.webrtc_ip_vs_observed весом 0.85 не закрывается чтением одного browser.webrtc_public_ip. И нужно сохранять идентичность правила, а не счётчик, потому что 31 правило, распределённое на 23 согласованности, 4 репутации, 3 автоматизации и 1 артефакт, не сводится к числу, которое что-то значит само по себе.
Есть предел, который стоит назвать прямо. Профиль, который держит каждую поверхность страницы согласованной с объявленной целью, тем самым не меняет рукопожатие, кадр настроек или адрес, наблюдаемый на дальнем конце; это отдельные факты, которые производят отдельные части клиента. Читать отчёт со стороны страницы как доказательство о соединении значит преувеличивать измеренное, а читать сетевой отчёт как доказательство о документе совершает обратную ошибку. Два отчёта описывают два уровня, и утверждение о браузере в целом требует обоих.
Записать чтение
Сетевое чтение подтверждает узкое утверждение: эта версия набора правил и это соединение дали такие входы. Четыре поля сохраняют его проверяемым:
- Версия и дайджест набора правил, потому что правило, которое получает или теряет вход, меняет смысл деления без всякого изменения клиента.
- Уровень, нужный каждому правилу, потому что 25 из 31 не читают ничего со страницы, а отчёт без уровня подразумевает, что страница их покрыла.
- Измеренные сетевые значения для того соединения, которое действительно использовалось, потому что список протоколов, шифр и кадр настроек принадлежат этому соединению, а не браузеру вообще.
- Шесть сравнений, которым нужны обе стороны, помеченные как измеренные наполовину, если прочитано только значение страницы.
Открытые источники
- RFC 8446: TLS 1.3
- RFC 9113: HTTP/2
- RFC 7301: TLS ALPN
- MDN: Navigator.userAgentData
- MDN: Accept-Language
- BotBrowser: справка по согласованности автоматизации
Практический вывод
Читайте отчёт, где находки перечислены вместе с уровнем, который дал каждую, потому что проверка со стороны страницы описывает 95 из 126 правил и не описывает 31, читающее соединение. Считайте шесть правил, сопоставляющих значение страницы со значением сети, закрытыми наполовину всякий раз, когда измерена только страница: сравнению нужны оба входа, а страница даёт один. Храните версию набора правил и измеренное соединение вместе с любой сетевой находкой, потому что список протоколов, набор шифров и кадр настроек принадлежат этому соединению, а не браузеру в целом. Про оценку, которая решает, может ли сработавшее правило вынести решение, читайте почему одна странная сигнал не выносит приговор браузеру; про то, как библиотека группирует свои находки до любого вердикта, читайте группы сигналов CreepJS и их объединение.
Эта статья входит в руководство: Что такое отпечаток браузера?
Похожие статьи
Почему один странный сигнал не выносит вердикт
ОтпечаткиЧто на самом деле проверяют публичные наборы тестов
ОтпечаткиФингерпринтинг Network Information API: проверка политики
СетьDNS over TLS: приватность резолвера, развертывание и границы сбоев
СетьСетевая согласованность браузера для защиты приватности
ОтпечаткиФингерпринтинг Navigator: утечки браузера
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.