🧊 Как DPI «замораживает» VLESS+REALITY: разбор схемы ограничений (июнь 2026)
Первоисточник
Этот разбор основан на статье Петра Осетрова (@hyperion_cs) на Хабре: 👉 «О схеме ограничений РКН в июне 2026-го» — habr.com/ru/articles/1044396
Все числовые параметры (пороги, тайминги, списки фингерпринтов) взяты из этой статьи. Ниже — простое объяснение «на пальцах» плюс расширенные технические детали по uTLS и mux от себя.
Дисклеймер
Материал носит исследовательский и образовательный характер. Задача — понять, по каким признакам современный DPI отличает обходные средства от обычного трафика, и почему одной волшебной галочкой проблему больше не решить.
Важно про статус этих данных
Описанная схема — это результат реверс-инжиниринга: автор сам называет её «Siberian» и добавил одноимённый чекер в dpi-checkers. Это наблюдения одного исследователя; сам автор оговаривает, что параметры метода различаются от оператора к оператору и меняются со временем.
Параллельно, в тот же период, задокументированы и другие механизмы блокировки — заморозка соединения после ~16 КБ / ~25 пакетов (метод «tcp 16-20»), CIDR-whitelist по подсетям назначения, SNI-whitelist (см. net4people/bbs #490). Поэтому всё ниже — снимок одной наблюдаемой волны на июнь 2026, а не универсальный закон. Конкретные числа читайте как «по наблюдениям автора», а не как точные константы системы.
TL;DR — если совсем коротко
Летом 2026 года перестала надёжно работать связка xray + VLESS + REALITY.
Важно: REALITY не взломали. Его шифрование по-прежнему неотличимо от настоящего TLS. Сломалось другое: DPI перестал заглядывать внутрь пакета (это бесполезно) и начал смотреть на поведение соединения снаружи.
Блокировка включается, только когда совпали сразу три признака (это важно — именно «И», а не «ИЛИ»). Нарушьте любой один — и правило на вас не сработает.
Объясняю на пальцах: что вообще произошло
Представьте таможню на границе (это наш DPI).
Раньше таможенник открывал каждый чемодан и смотрел, что внутри. REALITY — это чемодан с идеальной маскировкой: снаружи и внутри он выглядит как чемодан обычного туриста, едущего на www.microsoft.com. Подделку не видно, потому что для этого нужен секретный ключ, которого у таможни нет. Поэтому годами всё работало: «внутри чисто — проходи».
Теперь таможенник перестал открывать чемоданы (всё равно бесполезно) и начал смотреть на поведение пассажира:
- Откуда приехал? — если из района, где живут одни «контрабандисты» (популярные хостинги), это подозрительно.
- Как выглядит? — если одет в форму, в которой обычно ходят нарушители (фингерпринт Chrome), это подозрительно.
- Как себя ведёт? — если бегает туда-сюда через границу 10 раз в минуту (лавина соединений), это подозрительно.
И только когда все три совпали — пассажира «тормозят». Криптография тут уже ни при чём: ловят не содержимое, а манеру.
Алгоритм блокировки: три сигнала и один триггер
Цензор оценивает ClientHello — самый первый пакет TLS-рукопожатия — у каждого TLS-соединения. Среди прочего он смотрит на такие атрибуты:
Что оценивает цензор (из первоисточника)
- IP-адрес сервера (точнее — его подсеть);
- SNI (Server Name Indication — то самое замаскированное имя);
- фингерпринт «браузера» (точнее — того, под кого мимикрируют; у REALITY это делает uTLS).
К этим статическим атрибутам добавляется ещё и поведение — частота и параллелизм соединений. «Заморозка» включается, только когда совпали сразу несколько признаков (логическое «И»): подозрительная подсеть + подозрительный фингерпринт + аномальная частота к одному SNI. Разберём их по очереди.
Почему сигналов «три», хотя атрибутов перечислено больше
SNI в списке выше — не отдельный сигнал, а ключ агрегации для поведенческого Сигнала 3: частота соединений считается раздельно по каждому SNI. Поэтому ключевых триггеров остаётся три — подсеть, фингерпринт и частота к одному SNI.
Сигнал 1. Подсеть сервера — «откуда» вы подключаетесь
DPI смотрит на IP-адрес вашего сервера и проверяет, не входит ли он в список «подозрительных» подсетей. В этот список массово попали диапазоны популярных хостингов, в том числе российских:
- Selectel
- Яндекс.Облако
Логика простая: именно там дешевле и проще всего поднять свой узел, поэтому там их больше всего.
Важный нюанс: схема завязана не на конкретные IP отдельных узлов, а на целые подсети и даже автономные системы (AS).
Из первоисточника
Схема не завязана напрямую на конкретные IP промежуточных узлов (напр., ваших серверов). Это, в общем-то, неплохо — значит, узлы не улетели в некий чёрный список (особенно в свете того, что некоторые приложения «стучат»). Плохая новость в том, что затронуты целые подсети и даже AS, в т.ч. популярных российских дата-центров (Selectel, Яндекс.Облако и др.). Это заметно отличается от прежних методов, которые задевали только зарубежных провайдеров (tcp 16-20, aka l4-25, и пр.).
Что это значит на практике: ваш конкретный IP в чёрный список не попадает (его не «сожгли») — под подозрением сразу весь диапазон хостинга. Хорошая сторона — сменить IP в пределах того же провайдера не поможет, но и «гореть» вашему адресу не за что. Плохая — отсюда же берётся сопутствующий ущерб для легальных соседей по подсети (см. раздел про обычный Chrome ниже).
Что это значит для вас
Сервер в «народном» дата-центре — первый красный флаг. Чем менее предсказуемы провайдер и подсеть, тем лучше. Практику выбора см. в VPS.
Сигнал 2. TLS-фингерпринт клиента — «чем» вы подключаетесь
Когда ваша программа начинает TLS-соединение, она первым делом отправляет пакет ClientHello. В нём — список поддерживаемых шифров, расширений, их порядок и прочие технические мелочи. Этот набор у каждой реализации TLS свой, как почерк. По нему можно понять, какая именно программа сгенерировала пакет (подробнее — в разделе про uTLS ниже).
Как выглядит этот «почерк» на самом деле
Возьмём эталонный ClientHello из RFC 8448 — это пример прямо из стандарта, поэтому каждый байт можно сверить.
1. Текстовая форма — то, как пакет разбирает Wireshark/tshark (дерево полей):
TLSv1.3 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22) # 0x16
Version: TLS 1.0 (0x0301) # версия RECORD-слоя (легаси, для совместимости)
Length: 196
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 192
Version: TLS 1.2 (0x0303) # legacy_version — НЕ настоящая версия (см. ниже)
Random: cb34ecb1 e78163ba 1c38c6da … # 32 байта
Session ID Length: 0
Cipher Suites Length: 6
Cipher Suites (3):
TLS_AES_128_GCM_SHA256 (0x1301)
TLS_CHACHA20_POLY1305_SHA256 (0x1303)
TLS_AES_256_GCM_SHA384 (0x1302)
Compression Methods: null (0)
Extensions Length: 145
Extension: server_name → "server" # SNI
Extension: renegotiation_info
Extension: supported_groups → x25519, secp256r1, secp384r1, …
Extension: session_ticket
Extension: key_share → x25519 (32-байтный публичный ключ)
Extension: supported_versions → TLS 1.3 (0x0304) # ← НАСТОЯЩАЯ версия
Extension: signature_algorithms → ecdsa_secp256r1_sha256, …
Extension: psk_key_exchange_modes
Extension: record_size_limit2. Байтовая форма — тот же пакет «на проводе» (hex с аннотациями):
16 03 01 00 c4 TLS-запись: Handshake, ver 0x0301, длина 196
01 00 00 c0 ClientHello, длина 192
03 03 legacy_version = 0x0303 (TLS 1.2)
cb 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 ┐
6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 │ Random (32 байта)
02 4d ec e7 ┘
00 Session ID length = 0
00 06 13 01 13 03 13 02 Cipher Suites (6 б): 1301 1303 1302
01 00 Compression: 1 метод = null
00 91 Extensions, длина = 145 байт
00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 server_name → "server"
ff 01 00 01 00 renegotiation_info
00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 … supported_groups
00 23 00 00 session_ticket
00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 … key_share (x25519)
00 2b 00 03 02 03 04 supported_versions → 0x0304 (TLS 1.3)
00 0d 00 20 00 1e 04 03 05 03 … signature_algorithms
00 2d 00 02 01 01 psk_key_exchange_modes
00 1c 00 02 40 01 record_size_limitФингерпринт — это набор и порядок вот этих cipher suites и extensions. Заметьте: 73 65 72 76 65 72 в hex — это ASCII-строка server (тот самый SNI, видный открытым текстом). Реальный Chrome выглядел бы иначе: GREASE-значения, расширения ALPN и ECH, post-quantum key_share (X25519MLKEM768), другой порядок полей. Именно этот «слепок» и сворачивают в JA3/JA4 (см. раздел про uTLS), по нему DPI и опознаёт, кто перед ним.
Мелкий, но важный нюанс про версию
Обратите внимание: в поле версии стоит
0x0303(TLS 1.2), хотя это TLS 1.3. Так сделано нарочно — ради совместимости со старыми серверами. Настоящую версию несёт расширениеsupported_versions(0x0304). Поэтому JA4 берёт версию TLS именно оттуда, а не из легаси-поля.
И вот ловушка: xray умеет притворяться чужим почерком, и по умолчанию почти все ставили Chrome — он самый популярный, прятаться логичнее в толпе. В итоге сочетание «почерк Chrome + признаки прокси» само стало маркером.
| 🚩 Подозрительные фингерпринты | ✅ «Лояльные» фингерпринты |
|---|---|
| Chrome | Firefox |
| Safari | Edge |
| iOS | Android OkHttp |
| 360 Browser | |
| QQ Browser |
Парадокс
Самый популярный браузер планеты оказался самым «палевным» — именно потому, что под него массово маскируются. Безопаснее то, под что маскируются реже.
Сигнал 3. Частота и параллелизм — «как» вы подключаетесь
Третий признак — поведенческий, и именно он чаще всего вас и выдаёт. Точная формулировка триггера из первоисточника:
более 3 параллельных попыток установить TLS-соединение, с задержкой между ними менее ~350-400 мс, за последние 60 секунд — к одному и тому же SNI.
По этому порогу гуляют устаревшие цифры
Первоисточник правился трижды и без пометок: 6 июня 2026 там стояло «~100 мс», 7 июня — «~20-50 мс», и лишь 9 июня появилось нынешнее «~350-400 мс», которое держится с тех пор. Промежуточный вариант успел разойтись по перепечаткам и попал в англоязычный пересказ в обсуждении Xray-core #6293, снятый за сутки до правки. Если встретите «20-50 мс» — это старый срез. Актуальное значение подтверждается и свежим предложением #6547 от 27 июля 2026, где параметр
hMinSocketIntervalпредлагают выставлять в 400–600 мс именно с запасом над этим порогом.
Разберём по словам:
- более 3 соединений — то есть 4 и больше;
- задержка между ними < ~350-400 мс — то есть они идут залпом, почти одновременно;
- за последние 60 секунд — окно, в котором DPI это считает;
- к одному SNI — все на одно и то же замаскированное имя (например,
www.microsoft.com).
Откуда берётся такой залп? Это типичный почерк прокси. Вы открываете браузер, в нём десяток вкладок — и каждая инициирует своё соединение. Все они мультиплексируются на один замаскированный SNI. Живой человек, реально открывший www.microsoft.com, так себя не ведёт — он не создаёт 10 одновременных коннектов на этот домен.
Что это значит для вас
Лавина одновременных соединений на один SNI — поведенческая аномалия. Лечится мультиплексированием (
mux) и распределением по разным SNI (см. раздел про mux ниже).
Что происходит при срабатывании
Когда совпали все три условия, трафик к узлу «замораживается» на 120 секунд. Важная деталь: соединения не рвутся демонстративно (это было бы слишком явной цензурой) — они тихо деградируют. Растут таймауты, падает скорость, всё «как будто само тормозит». Внешне похоже на плохой интернет, а не на блокировку.
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
TLS- │ подсеть в │ И │ фингерпринт │ И │ >3 conn/SNI │ ➜ ЗАМОРОЗКА
соединение │ списке? │ │ подозрит.? │ │ <~350-400мс/60с│ 120 сек
───────► │ (Selectel, │ │ (Chrome, │ │ │
│ Я.Облако…) │ │ Safari,iOS) │ │ │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
разорвите ЛЮБОЕ одно звено ➜ правило на вас НЕ срабатывает
Почему обычный Chrome работает, хотя «лояльным» его не назвали
Здесь возникает резонный вопрос: если фингерпринт Chrome — «подозрительный», почему миллионы людей спокойно сидят в обычном Chrome и ничего не тормозит? А заодно — почему при этом у части людей вдруг ломаются некоторые российские сайты?
Ответ — снова в логике «И трёх условий». Вспомните: чтобы поймать заморозку, нужны все три сигнала одновременно. У обычного веб-сёрфинга почти всегда не выполняется первый — про подсеть.
Почему Chrome обычно работает. Когда вы просто листаете интернет, вы ходите на реальные сервисы — Google, YouTube, банки, маркетплейсы. Они живут на собственной инфраструктуре и больших CDN, которые в список «подозрительных подсетей» не входят (а часто и вовсе в белых списках). Получается:
- 🚩 Фингерпринт Chrome — да, подозрительный (Сигнал 2 ✅);
- лавина параллельных соединений — да, браузер их открывает (Сигнал 3 может сработать);
- но подсеть назначения — чистая (Сигнал 1 ❌).
Одно звено цепи разорвано → правило не срабатывает. Именно поэтому Chrome для обычного веба «прозрачен»: его спасает не фингерпринт, а адрес назначения. А вот ваш личный VLESS-сервер на Selectel/Яндекс.Облаке этот первый сигнал как раз включает — отсюда и разница.
Почему всё-таки ломаются некоторые ру-сайты. Это оборотная сторона грубой эвристики «по подсети». В списки подозрительных попали диапазоны российских хостингов (Selectel, Яндекс.Облако), а на этих же хостингах живёт масса легальных сайтов и сервисов — тех, кто просто арендует там сервер. DPI не умеет отличить «прокси на Selectel» от «интернет-магазина на Selectel» — для него это одна подсеть.
И если вы в обычном Chrome заходите на такой легальный сайт, расположенный на «помеченном» хостинге, может совпасть всё три:
- сайт на подозрительной подсети (Сигнал 1 ✅);
- вы в Chrome (Сигнал 2 ✅);
- браузер открыл несколько быстрых параллельных соединений к его домену (Сигнал 3 ✅).
→ Получаем ложное срабатывание: легальный сайт начинает тормозить, хотя никто ничего не обходил. Это и есть «сопутствующий ущерб» (collateral damage) схемы: блокировка по подсети неизбежно задевает добросовестных соседей по хостингу.
Коротко
Обычный Chrome спасает чистая подсеть назначения, а не сам браузер. Где подсеть «грязная» (ваш VPS или легальный сайт на том же хостинге) — там при Chrome и залпе соединений ловят всех подряд, включая невиновных.
Интересный нюанс: ловушка для тех, кто «дёргается»
Самая хитрая часть схемы — реакция на попытку быстро поменять настройки.
Допустим, вас «заморозили». Вы решаете: «наверное, дело в фингерпринте» — и тут же переключаете Chrome на Firefox, переподключаясь. Логично же? Но для DPI это отдельный сигнал: только что соединение получило деградацию — и человек моментально сменил TLS-почерк. Так ведёт себя именно тот, кто осознанно обходит ограничение.
В ответ прилетает расширенная блокировка на 600 секунд — и уже на все TLS-соединения к этому узлу. Важно: на эти 10 минут замораживается любой TLS, независимо и от фингерпринта, и от SNI — сменить почерк или замаскированное имя уже не поможет. Сам TCP-коннект при этом проходит (рукопожатие на уровне TCP устанавливается), душится именно TLS поверх него.
Мораль
Система ловит не только сам обход, но и паттерн адаптации. Нервно крутить настройки под нагрузкой — делать себе хуже. Правильная стратегия: не реагировать рефлекторно, а изначально не попадать в цепочку.
🔬 Технические детали: uTLS — как работает TLS-почерк
Раздел для тех, кто хочет понять механику, а не просто поставить галочку.
Что такое JA3/JA4 и почему по TLS видно программу
ClientHello — это первый, ещё не зашифрованный пакет TLS-рукопожатия. В нём открытым текстом передаются:
- версия TLS;
- список cipher suites (наборов шифров) — и в каком порядке;
- список extensions (расширений: SNI, ALPN, supported_groups, signature_algorithms, key_share и т.д.) — и в каком порядке;
- эллиптические кривые, форматы точек, GREASE-значения и пр.
Конкретная программа (Chrome, Firefox, Go-приложение, curl) собирает этот набор по-своему: свой порядок шифров, свой набор расширений. Если взять все эти поля и прогнать через хеш, получится отпечаток. По нему DPI с высокой точностью говорит: «это Chrome 124» или «это Go-клиент».
Два основных формата отпечатков:
-
JA3 (старый) — это MD5-хеш от полей
ClientHello, взятых в том порядке, в каком они идут в пакете. Минус: Chrome специально тасует порядок расширений от соединения к соединению, поэтому JA3-хеш у него «плавает» и легко обходится. -
JA4 (новый, более устойчивый) — как раз поэтому он сортирует шифры и расширения перед хешированием и игнорирует GREASE. Это не один хеш, а строка из трёх частей через
_(a_b_c):- a (читаемая): протокол (
t— TLS-over-TCP,q— QUIC,d— DTLS), версия TLS, есть ли SNI (d= domain /i= IP), число шифров, число расширений и ALPN (первый и последний символ значения первого ALPN, напр.h2); - b: усечённый SHA256 от отсортированных cipher suites;
- c: усечённый SHA256 от отсортированных extensions (без SNI и ALPN), за которыми идут signature algorithms — уже без сортировки.
За счёт сортировки шифров и расширений JA4 не «обманывается» их перетасовкой; при этом signature algorithms намеренно оставлены несортированными как дополнительный различающий признак.
- a (читаемая): протокол (
Вот в чём беда TLS-обхода на Go: стандартная библиотека crypto/tls имеет очень узнаваемый фингерпринт — это прямо признают сами разработчики uTLS («Golang’s ClientHello has a very unique fingerprint»). В реальном вебе такой почерк почти не встречается → мгновенный маркер прокси. Поэтому обычный VLESS/Trojan поверх TLS, если не указать fingerprint, спалится сразу.
Контраст «почерков»: голый Go crypto/tls vs Chrome
Чтобы было видно, насколько они различаются — ключевые отличия в ClientHello (структурно; точные байты зависят от версии):
| Признак | 🦫 Голый Go crypto/tls | 🌐 Chrome 131+ |
|---|---|---|
| GREASE | нет нигде | в 5 местах: первый шифр, первое и последнее расширение, supported_versions, supported_groups, key_share |
| Порядок расширений | жёстко фиксирован | тасуется на каждом соединении (с Chrome 110, янв. 2023) |
application_settings (ALPS) | нет | есть |
compress_certificate (brotli) | нет | есть |
ECH (encrypted_client_hello) | нет (по умолчанию) | есть (GREASE-ECH или настоящий) |
| Число расширений | ~10–12 | ~17–18 |
| Cipher list | 13 шифров, сразу с TLS 1.3 AEAD | начинается с GREASE, характерная браузерная раскладка |
Любого из верхних трёх отличий (нет GREASE, фиксированный порядок, нет ALPS/brotli/ECH) уже достаточно, чтобы пассивно отличить Go-клиент от Chrome. Поэтому «голый» Go без uTLS — мгновенный маркер. (Нюанс актуальности: post-quantum key_share X25519MLKEM768 Chrome шлёт с v131, а Go — только с 1.24; на старых версиях Go отсутствие PQ было ещё одним отличием.)
Важный нюанс про REALITY
В REALITY uTLS встроен и обязателен: поле
fingerprintпомечено как Required, а режим отключения uTLS (unsafe) официально не поддерживается — REALITY использует эту библиотеку для манипуляции низкоуровневыми параметрами TLS. Поэтому «голый»crypto/tls-почерк от REALITY никогда не уходит — он всегда отправляет валидный фингерпринт браузера. Проблема REALITY в июне 2026 — не в Go-почерке, а в том, что почерк хоть и корректный, но слишком массовый (chrome). Об этом — ниже.
Что делает uTLS
uTLS (refraction-networking/utls) — это форк стандартного crypto/tls, который позволяет подменить содержимое ClientHello: порядок шифров, набор расширений, GREASE — по готовому шаблону настоящего браузера. То есть xray генерирует ClientHello, неотличимый от Chrome или Firefox, и JA3/JA4-хеш совпадает с настоящим браузером. (Технически uTLS работает на уровне готовых пресетов-спецификаций; полный побайтовый контроль возможен, но в обычном режиме хватает пресета, чтобы хеши совпали.)
В конфиге xray это поле fingerprint внутри realitySettings. Кстати, поле с публичным ключом сервера в актуальных версиях xray-core называется password (раньше — publicKey; старое имя пока работает для совместимости). uTLS поддерживает пресеты вроде HelloChrome_Auto, HelloFirefox_Auto, HelloEdge_Auto, HelloSafari_Auto, HelloRandomized и др. В xray они задаются короткими именами: chrome, firefox, edge, safari, ios, android, 360, qq, random, randomized. То есть рекомендованные в таблице выше «лояльные» 360 Browser и QQ Browser пишутся как "fingerprint": "360" и "fingerprint": "qq".
"realitySettings": {
"fingerprint": "firefox",
"serverName": "www.microsoft.com",
"password": "<публичный ключ сервера>",
"shortId": "<short id>"
}Почему это перестало спасать само по себе
uTLS делает почерк корректным, но не делает его редким. Если все массово ставят chrome, то «JA3 Chrome + поведение прокси» становится статистически заметным. DPI не говорит «фингерпринт поддельный» (он валидный!) — он говорит «слишком много именно такого фингерпринта летит на этот странный сервер залпами». Поэтому в июне 2026 совет сместился: брать не самый массовый профиль — firefox/edge и пр.
Про
randomиrandomized— это РАЗНЫЕ режимыИх легко перепутать, но ведут себя они по-разному:
random— xray случайно берёт один из готовых реальных пресетов браузера (Chrome 131, Firefox 148 и т.п.). Почерк всегда валидный — это безопасный вариант.randomized(иrandomizednoalpn) — синтетическая генерация: расширения и шифры добавляются/тасуются случайно. Может получиться комбинация, которой не существует ни у одного реального браузера — а это само по себе аномалия.Вывод: бездумно ставить
randomizedне стоит. Лучше явный «лояльный» пресет (firefox/edge) или, на худой конец,random— но не синтетическую рулетку.
uTLS не маскирует тайминги
Ключевой момент: uTLS правит только содержимое ClientHello. Он никак не влияет на Сигнал 3 — на то, сколько соединений и с какой частотой вы открываете. Почерк может быть идеальным, но если вы шлёте на один SNI залп коннектов с интервалом между ними менее ~350-400 мс — вас всё равно поймают по поведению. Вот почему одного uTLS мало, и нужен mux.
🔬 Технические детали: mux — как сгладить лавину соединений
mux (мультиплексирование) — это прямое лекарство от Сигнала 3.
Проблема, которую он решает
Без mux каждая вкладка / каждое приложение = отдельное физическое TCP+TLS-соединение до сервера. Открыли почту, мессенджер, пару сайтов — и вот уже залп одновременных коннектов на один замаскированный SNI. Ровно тот паттерн, который ловит DPI.
Как работает mux в xray
Мультиплексирование загоняет много логических потоков внутрь одного физического соединения. Снаружи DPI видит одно TLS-соединение, по которому идёт постоянный поток данных, — как у обычного пользователя, открывшего тяжёлый сайт. Залпа новых рукопожатий нет → Сигнал 3 не срабатывает.
"mux": {
"enabled": true,
"concurrency": 8,
"xudpConcurrency": 16,
"xudpProxyUDP443": "reject"
}Разберём параметры:
concurrency— сколько логических потоков пускать в одно физическое соединение (для TCP). Допустимый диапазон —1…128, по умолчанию8. Когда лимит исчерпан, открывается новое физическое соединение. Любое отрицательное значение (-1) отключает mux для TCP.xudpConcurrency— то же самое, но для UDP-трафика поверх механизма XUDP (нужно для игр, звонков, QUIC). Диапазон у него другой —1…1024(а не 1…128, как у TCP); при0или отсутствии XUDP-трафик идёт по тому же пути, что и обычный TCP.xudpProxyUDP443— что делать с UDP на 443-м порту (это QUIC/HTTP3). Часто ставятreject, чтобы QUIC падал обратно на TCP — так трафик идёт по нашему замаскированному каналу, а не утекает мимо.
У mux есть и обратная сторона
- Head-of-line blocking: все логические потоки делят одно TCP-соединение. Потеря одного пакета тормозит сразу все потоки внутри. На нестабильном канале это ощутимо бьёт по скорости.
- Слишком большой
concurrencyсклеивает весь трафик в один долгоживущий коннект — тоже своего рода аномалия (реальные браузеры всё же открывают несколько соединений). Истина посередине: умеренные значения вроде4–8.- Для скоростных загрузок mux иногда наоборот мешает — один коннект упирается в лимиты. Поэтому его часто включают выборочно.
mux и XTLS Vision: TCP-mux не применяется
Это важно именно для нашей связки. Сейчас стандартный поток для VLESS+REALITY — XTLS Vision (
"flow": "xtls-rprx-vision"). Vision работает на «сыром» (raw) TCP-соединении соsplice-копированием в ядре Linux, поэтому TCP-mux при Vision штатно не применяется — это поведение самого Xray-core, а не сбой (в формулировке ядра: «MUX is not compatible with XTLS raw connections»).На практике клиенты при Vision генерируют такой блок:
"enabled": true,"concurrency": -1(TCP-mux выключен), но"xudpConcurrency"оставлен — чтобы мультиплексировать хотя бы UDP/XUDP. Учтите: при"enabled": falseXUDP-mux тоже не активируется.Вывод: не копируйте
"concurrency": 8бездумно поверх Vision-конфига. Если у вас Vision, бороться с Сигналом 3 придётся другими средствами — в первую очередь разнесением по разным SNI и сдержанным паттерном соединений.
Дополнительно: разносите SNI
Триггер Сигнала 3 считается на один SNI. Если раскидать трафик по нескольким разным SNI (см. большой список в vless-sni), нагрузка не концентрируется на одном имени, и порог «>3 соединений с интервалом <~350-400 мс за 60 с» на каждое отдельное имя достигается труднее. mux + несколько SNI вместе очень эффективно сглаживают поведенческий след.
✅ Что делать: рвём цепочку (по шагам)
Поскольку триггер — это «И» трёх условий, для безопасности достаточно стабильно нарушать хотя бы одно звено. На практике стоит закрыть несколько — для запаса прочности.
Шаг 1. Сменить фингерпринт на «лояльный»
Самое дешёвое действие. В realitySettings поменять fingerprint с chrome на firefox или edge:
"realitySettings": {
"fingerprint": "firefox"
}Шаг 2. Включить mux и снизить параллелизм
Сглаживаем залп соединений:
"mux": {
"enabled": true,
"concurrency": 8
}И дополнительно — разнести трафик по нескольким SNI (список SNI).
Если у вас XTLS Vision
При стандартном для REALITY flow
xtls-rprx-visionTCP-mux не применяется (см. раздел про mux и Vision выше), так чтоconcurrency: 8тут не сработает. Тогда против Сигнала 3 работают только разные SNI + сдержанный паттерн, либо переход на XHTTP+XMUX (см. FAQ «Можно ли вылечить обе болезни сразу?»).
Шаг 3. Увести сервер в «невидимую» подсеть
Уходим из массовых диапазонов народных хостингов (Selectel, Яндекс.Облако и т.п.). Менее предсказуемый провайдер/подсеть → выпадаем из списка «подозрительных». Подбор — в VPS.
Шаг 4. Не дёргаться под нагрузкой
Поймали деградацию — не меняйте фингерпринт на лету. Лучше переждать окно (120 с) или сменить узел целиком. Оговорка по актуальности: длинную десятиминутную блокировку автор наблюдений в обновлении от 16 июня 2026 отметил как, судя по всему, убранную — но осторожность здесь всё равно дешевле эксперимента.
«Смени фингерпринт» — не единственный и не универсальный рецепт
Совет про
firefox/edge— снимок одной волны, и он не гарантирован. Что важно держать в голове:
- Свежесть пресета uTLS важнее выбора браузера. К концу 2025 — началу 2026 более половины «человеческих» браузерных соединений (по данным Cloudflare Radar) несут post-quantum key_share (
X25519MLKEM768) — он стал дефолтом в Chrome 131 (ноя 2024) и Firefox 132 (окт 2024). Пресет без него, выдающий себя за свежий браузер, уже сам по себе аномален — следите, чтобы uTLS/xray были свежими.- Не путайте с обходами ДРУГИХ блокировок. Советы «пустой SNI» и «нестандартный порт (47000+)» относятся к более ранней сигнатурной блокировке на порту 443 (нач. 2025) и к ограничению по числу TLS-соединений — «сибирскую» схему (подсеть + фингерпринт + частота) они логически не нарушают.
XHTTPпомогает лишь как способ снизить число соединений (это уже делаетmux), а Shadowsocks-2022 — не обход, а смена протокола (у него нет ни SNI, ни uTLS-почерка браузера). Столкнулись именно с этой схемой — работает только нарушение её трёх условий.
Проверить себя: dpi-checkers
Автор первоисточника ведёт инструмент dpi-checkers (на момент разбора — версия v0.7.0). Он прогоняет вашу инфраструктуру по описанным критериям: попадает ли подсеть в подозрительные, как выглядит фингерпринт, ловится ли поведенческий паттерн. Проверки задаются в YAML, например фильтр по организации хостинга:
checkers:
webhost:
infra:
- name: Selectel
filter: org("selectel")❓ Слабые места и частые вопросы
Несколько вопросов, которые закономерно возникают после прочтения.
Фиксированный список фингерпринтов uTLS — это слабое место?
Да. firefox, который у вас «снова заработал», — это снимок конкретной версии (например HelloFirefox_120). И его тоже могут начать ловить. Почему список — структурная слабость:
- Устаревание. Пресет заморожен во времени, а живой браузер обновляется каждые недели.
HelloChrome_120со временем становится редкой старой версией на фоне Chrome 13x — особенно теперь, когда у свежих браузеров появился post-quantumkey_share, которого у старого пресета нет. - Перечислимость. Список открытый и небольшой (~50 пресетов в
u_common.go). Цензор может просто выписать все JA3/JA4 пресетов uTLS и сверять с распределением реальных браузеров. - Несовершенство «попугая». Даже совпав по версии, uTLS иногда отличается в деталях — вплоть до уязвимостей с CVE: например рассогласование GREASE/ECH-шифров в Chrome-пресете давало комбинацию, невозможную у настоящего Chrome (CVE-2026-27017, закрыто в uTLS 1.8.1).
Что вас пока спасает: заблокировать популярный валидный фингерпринт целиком цензору дорого — сломаются реальные пользователи Firefox/Chrome. Слабость возникает, когда отпечаток одновременно коррелирует с прокси и достаточно редкий, чтобы блок был «дешёвым».
Можно отсканировать свой реальный браузер и залить отпечаток в uTLS-клиент?
И да, и нет — важно разделить библиотеку и конечный клиент:
- В библиотеке uTLS — можно.
Fingerprinter.FingerprintClientHello(rawBytes)берёт сырые байты реальногоClientHello(захваченные Wireshark’ом — не JA3-хеш, он «лоссовый»), строитClientHelloSpec, применяемый черезHelloCustom. - В xray-core и sing-box — НЕЛЬЗЯ. Оба ограничены меню пресетов; подсунуть свой захваченный
ClientHelloв конфиг нельзя. Это просили напрямую — xray-core issue #1782 закрыт как «not planned». - Сканеры своего браузера живы: browserleaks.com/tls (самый подробный), tls.peet.ws (JSON-API). Но они выдают хеши/сводку, а не raw-байты, нужные
Fingerprinter.
Итого: готового пайплайна для обычного пользователя нет — это ручная работа разработчика (pcap → FingerprintClientHello → свой Go-код). В стандартном клиенте доступны только пресеты + random/randomized.
Парадокс уникальности
Если зальёте свой уникальный отпечаток — он станет маркером именно вас. Сила браузерного фингерпринта в том, что он массовый; уникальный — наоборот, деанонимизирует.
Альтернатива uTLS-пресетам: реальный стек Chromium (naive/cronet)
Раз uTLS — это попугай (заморожённый пресет, который перечислим и со временем протухает, а ручной скан своего браузера в стандартный клиент не залить), напрашивается другой путь: не имитировать браузер, а взять настоящий сетевой стек браузера. Тогда JA3/JA4 — не копия, а подлинник, и обновляется он вместе с браузером.
Это удобно представить как лестницу аутентичности почерка (от худшего к лучшему):
| Подход | Почерк | Свежесть | Мультиплексирование |
|---|---|---|---|
uTLS-пресет (chrome/firefox) | копия, перечислимая | протухает (нужно ждать новый пресет) | нет (нужен отдельный mux) |
NaiveProxy (Chromium //net/cronet) | подлинный стек Chromium | отстаёт: автор вручную линкует свежий хромиум; на момент обсуждения был Chrome 149, а Naive — на сборке ~148.x | да, естественный HTTP/2 |
| XHTTP + Browser Dialer (Xray) | подлинный — TLS делает реально установленный в системе браузер | моментальная: берётся любой обновлённый браузер из системы | да, в паре с XMUX (maxConnections) |
Закрывает сразу две оси — но двумя рычагами, не «одной галочкой»
Подлинный стек браузера даёт массовый настоящий фингерпринт — это Сигнал 2 (дорого банить, не «протухает», как пресет). А вот поведенческую ось (Сигнал 3) закрывает не сам почерк, а транспорт: HTTP/2 +
XMUX. У NaiveProxy мультиплексирование нативное (HTTP/2 в cronet); у Browser Dialer его нужно включить отдельно —XMUX/maxConnectionsв XHTTP, само по себе оно не появляется. То есть это два рычага в связке (подлинный почерк + mux), а не один инструмент «из коробки» — зато оба живут в одном клиенте, тогда как «uTLS + отдельный mux» собираются из более хрупких частей. Про поведенческую ось — см. раздел про mux в этой заметке;XMUXв xhttp придумали ещё до «сибирской» схемы — изначально ради пулинга соединений под CDN, но как побочный эффект это и сглаживает лавину (Сигнал 3).
Нюансы (по сообщениям сообщества, не как гарантия):
- NaiveProxy всё ещё не свободен от устаревания: cronet-стек влинкован под конкретную сборку Chromium, и пока автор не пересоберёт под новый — поведение свежего Chrome может отличаться в деталях. То есть Naive уменьшает разрыв с реальным браузером, но не обнуляет его.
- Browser Dialer использует уже установленный браузер, поэтому переход на новую версию мгновенный и попугайства нет вообще — но цена в том, что нужен живой браузер/вкладка-«диалер» рядом с клиентом (UX-издержка); по сообщениям, на Android рабочий вариант.
- Это клиентский путь: почерк генерится на устройстве пользователя, до цензора — ровно то, о чём «JA4 и SNI меняет только клиент». Серверная сторона тут по-прежнему ничего не решает.
Не «серебряная пуля»
Подлинный почерк снимает только Сигнал 2 (фингерпринт) и, через XMUX/HTTP-2, Сигнал 3 (частоту). Сигнал 1 (подсеть VPS) остаётся — его убирает только CDN-фронтинг (см. ниже «Что реально помогает против сигнала подсети»). И сам по себе подлинный стек не прячет TLS-в-TLS — это отдельная ось (Vision/padding).
Имеет ли смысл держать на VPS свою страничку и использовать её как SNI?
Для REALITY — нет, это ломает саму идею. REALITY не использует ваш домен — он заимствует TLS-личность чужого популярного сайта: вы указываете target/dest = реальный внешний сайт, REALITY ретранслирует к нему рукопожатие, и наблюдатель видит настоящий сертификат этого сайта. Своя страничка как SNI:
- теряет заёмную репутацию (а это весь смысл REALITY) — неизвестный
myhomepage.tldрепутации не имеет; - не входит в SNI-whitelist — при белых списках неизвестное имя подозрительнее знаменитого;
- концентрирует частоту — весь трафик на один свой SNI = ровно та аномалия «много к одному редкому имени», которую ловит Сигнал 3.
Decoy-сайт (своя страница + nginx-fallback) — это про классическую маскировку (Trojan / VLESS + реальный сертификат) и помогает он против активного зондирования, а не против этой пассивной схемы. Если уж REALITY напрямую на VPS — берите чужой знаменитый SNI, не свой.
Не устарел ли сам REALITY? (сверка пары SNI↔IP)
Отдельный довод, который звучит у практиков: REALITY становится уязвим к сверке пары SNI↔IP. Вся идея REALITY — заимствовать чужой знаменитый SNI (например www.microsoft.com), но соединение при этом идёт на ваш VPS-IP, а не на реальные адреса Microsoft. Пассивный цензор, ведущий карту «какой SNI обычно ходит на какие IP/ASN», может ловить рассогласование: в ClientHello заявлен www.microsoft.com, а пакет летит в случайную подсеть хостинга. Активное зондирование тут ни при чём — это пассивная проверка, и фирменная защита REALITY «отдать настоящий серт на пробу» от неё не спасает (это другая ось).
«REALITY бесполезен» — это гипербола
Сверка SNI↔IP дорога цензору: легитимных рассогласований масса (CDN, ECH, сплит-DNS, корпоративные прокси), поэтому массовая проверка даёт ложные срабатывания на обычном трафике — тот же «сопутствующий ущерб», что и с подсетями. Корректнее формулировать как «вектор реален и REALITY слабеет», а не «мёртв». И главное: это мнение/прогноз практиков, а не подтверждённая часть «сибирской» схемы — в первоисточнике три сигнала (подсеть, фингерпринт, частота), сверки SNI↔IP там нет.
Это родственная логика той, что осторожно отмечена для MTProto в проверке SNI на резолвимость: в обоих случаях цензор сверяет заявленное имя с реальностью соединения. Но сами проверки разные: для MTProto — резолвится ли SNI вообще (есть ли A-запись); для REALITY — совпадает ли SNI с IP назначения. И там, и там это пока гипотеза о развитии DPI, а не зафиксированное правило.
И ещё довод против «фирменности» REALITY: его защита от активного зондирования — это fallback (редирект неаутентифицированных проб на dest-бэкенд, «spider mode»), и поведенчески она воспроизводится десятком строк в caddy/nginx (обычный TLS + fallback на реальный сайт). То есть по-настоящему уникальна у REALITY именно заёмная личность чужого удалённого сайта — а её как раз и подтачивает сверка SNI↔IP. (Для ясности: self-steal — это не защита, а топология REALITY, где dest/target смотрит на свой локальный decoy, т.е. ровно тот самый «своя страница + nginx-fallback», только средствами REALITY — противоположность заимствованию чужого сайта.) При этом против самого опасного, Сигнала 1 (подсеть), REALITY не помогал никогда — поэтому общий вывод не меняется: структурный рычаг — CDN-фронтинг (см. ниже), а не выбор «магического» транспорта.
Что реально помогает против сигнала «подозрительная подсеть»?
Все локальные меры (свежий пресет, mux, разные SNI, выбор провайдера) не убирают Сигнал 1 — подсеть VPS остаётся подозрительной. Единственный рычаг, который структурно его убирает, — спрятать назначение за большим CDN:
- VLESS over XHTTP / WebSocket за Cloudflare. Цензор видит соединение к IP Cloudflare (огромный, де-факто whitelisted), а не к вашему VPS — сигнал подсети исчезает, потому что меняется сам адрес, который видит цензор.
- Цена честно: CDN терминирует ваш TLS и видит трафик в открытом виде; медленнее (крюк через edge); ToS-серая зона для тяжёлого/видео-трафика.
- Важно: сам REALITY за CDN так не ставится — ради CDN меняют транспорт (XHTTP/WS).
Что от чего помогает: active probing vs пассивная схема
| Мера | Против active probing | Против пассивной схемы (подсеть + фингерпринт + частота) |
|---|---|---|
| REALITY со знаменитым чужим SNI | ✅ | ⚠️ частично — знаменитый SNI размывает частоту, но подсеть не меняется |
| REALITY со своим decoy-SNI | ✅ | ❌ хуже — редкий SNI концентрирует трафик; подсеть та же; не в whitelist |
| Классика VLESS/Trojan + свой серт + nginx-fallback | ⚠️ частично | ❌ — свой домен + та же подсеть |
| CDN-фронтинг (VLESS+XHTTP/WS за Cloudflare) | ✅ | ✅ единственный рычаг, убирающий сигнал подсети (цена: CDN видит трафик, медленнее, ToS) |
Прямое подключение vs каскад — спасает ли это от Сигнала 1?
Короткий ответ: нет — решает не топология, а флагнутость подсети первого хопа.
Сигнал 1 смотрит не на вашу домашнюю подсеть (residential-IP в РФ), а на подсеть назначения того соединения, которое видит DPI — то есть на первый хоп, сервер, к которому вы открываете TLS.
- Прямое
юзер → eu-нода. DPI видит соединение к зарубежному серверу. И зарубежные хостинги тоже в списке подозрительных — более того, они там были изначально: старые методы (tcp 16-20/l4-25) били только по зарубежным провайдерам (Hetzner, DigitalOcean, OVH). Новизна «сибирской» схемы — что к ним добавили российские ДЦ, а не заменили. Так что прямое подключение к VPS на Hetzner/DO спокойно попадает под Сигнал 1. - Каскад
юзер → ru-нода → eu-нода. DPI видит вход — ru-ноду. Если она на Selectel/Яндекс.Облаке, это теперь тоже подозрительная подсеть, то есть каскад через российский ДЦ стало только хуже. (Хоп ru→eu — исходящий трафик сервера, ваш DPI его как клиентское рукопожатие не оценивает.)
Сигнал 1 не срабатывает не «при прямом» и не «при каскаде», а когда подсеть первого хопа вообще не в списке: residential/мобильные IP, большой CDN (поэтому CDN-фронтинг и есть структурный рычаг), редкий «чистый» провайдер. Hetzner напрямую и Selectel через каскад «горят» одинаково; residential или CDN — нет, в любой топологии.
Оговорка
Точный состав списка подозрительных подсетей публично неизвестен (реверс-инжиниринг одного автора). Вывод «зарубежные тоже в списке» следует из формулировки первоисточника («затронуты подсети/AS, в т.ч. российских ДЦ, в отличие от методов, бивших только по зарубежным»), а не из опубликованной спецификации.
Разве XTLS Vision не маскирует большое количество TLS-соединений?
Частая путаница. Нет — Vision маскирует не количество соединений, а форму TLS-в-TLS внутри одного соединения.
- Vision (
xtls-rprx-vision) решает проблему «TLS-in-TLS detection»: когда прокси несёт внутренние TLS-записи внутри внешнего TLS, возникает узнаваемый вложенный паттерн (характерные размеры записей). Vision добавляет padding в раннюю фазу рукопожатия и затем переключается на прямоеsplice-копирование в ядре — внешне трафик перестаёт выглядеть как «TLS в TLS». - Но Vision работает по-соединению и ничего не объединяет. Каждое проксируемое соединение — это всё равно свой отдельный внешний TLS+TCP-коннект к серверу. Браузер открыл 10 вкладок → 10 внешних TLS-соединений на один SNI. Это ровно территория Сигнала 3.
- Объединяет много логических потоков в одно физическое соединение — это
mux, а не Vision. И именно поэтому они конфликтуют (см. раздел про mux выше): Vision требует «сырого» TCP под splice, а mux оборачивает поток.
Вывод: Vision прячет вложенность TLS, но не уменьшает число соединений. Против поведенческого Сигнала 3 он не помогает — это задача mux (несовместимого с Vision) либо разнесения по разным SNI и сдержанного паттерна.
Именно из этой несовместимости вырос популярный слух «в России без flow работает лучше»: у части домашних провайдеров с ноября 2025 года помогала связка «пустой flow + mux», где отказ от Vision был техническим условием для mux, а не самостоятельным обходом. Разбор случая с первоисточниками — в карте слоёв VLESS-стека, раздел про пустой flow.
Vision, mux и «TLS-в-TLS» — три вещи, которые путают
Самый запутанный узел. Тут сходятся три независимых понятия — разведём их:
| Ось | Что это | Чем управляется |
|---|---|---|
Security (reality/tls) | чьей TLS-личностью представляется внешний туннель | выбор security |
| TLS-в-TLS | вложенность: сайт-TLS внутри туннеля | возникает сам при HTTPS-сёрфинге → лечит Vision |
| Число внешних соединений | сколько отдельных TCP+TLS к серверу | mux (вкл/выкл) → считает Сигнал 3 |
Главное: reality vs tls НЕ решает «отдельные коннекты или один поток» — это решает mux. И reality, и tls по умолчанию дают одно внешнее соединение на коннект и оба несут TLS-в-TLS.
К этим трём осям с конца августа 2025 добавилась четвёртая — encryption, то есть шифрование самого протокола VLESS. Маскировку она не заменяет: её собственный внешний вид спрятан внутри туннеля, а снаружи наблюдатель видит всё тот же внешний слой. Зато она снимает старое ограничение «Vision только на прямом TCP» и даёт настраиваемую набивку рукопожатия — RPRX в ноябре 2025 предлагал вложить её внутрь REALITY именно как способ поменять признаки трафика без правки кода. Полная матрица сочетаний — в карте слоёв VLESS-стека.
Что такое TLS-в-TLS (с этим борется Vision). Открываете https://google.com через туннель — получаются два вложенных слоя TLS:
[ внешний TLS / REALITY-туннель : вы ↔ ваш VPS ]
└── внутри: [ внутренний TLS : браузер ↔ google.com ] ← «TLS в TLS»Даже ОДНО соединение уже имеет эту вложенность: размеры записей внутреннего рукопожатия «просвечивают» сквозь внешний слой. Vision добавляет padding и переключается на splice, чтобы внутренний почерк не просвечивал. Это есть и при reality, и при tls.
А «один большой поток с кучей рукопожатий» — это mux (а не security: tls!):
[ ОДНО внешнее TLS-соединение ]
├── подпоток 1 → сайт A (внутри свой TLS)
├── подпоток 2 → сайт B
└── подпоток 3 → сайт CМного потоков в одном коннекте → меньше внешних соединений → бьёт по Сигналу 3. Но Vision на mux-поток не накладывается — ему нужен «сырой» отдельный TCP под splice, поэтому Vision и mux взаимоисключающие.
Развязка
- Vision лечит TLS-в-TLS (форму внутри одного соединения) — это отдельный детект, не из «сибирской» тройки.
- mux лечит число соединений (Сигнал 3).
- Они чинят разные болезни и взаимоисключающи — выбираете что-то одно.
security(reality/tls) — только про то, чьим сертификатом представляется туннель; к «коннекты vs поток» отношения не имеет.
Можно ли вылечить обе болезни сразу?
Раз Vision и mux взаимоисключающие — обречены ли мы выбирать? Нет. И тут важное переосмысление: сам конфликт — не «защита против защиты».
Vision склеивает две разные вещи:
- padding — добивает короткие пакеты ранней фазы рукопожатия → это и есть анти-детект TLS-в-TLS;
- splice — после рукопожатия копирует поток прямо в ядре Linux → это оптимизация производительности, и только.
С mux несовместим именно splice (ему нужен «сырой» 1:1 поток), а не padding. То есть настоящий трейд-офф — «padding + splice-скорость» против «мультиплексирования», а не «один детект против другого». Splice — это то, чем жертвуют, а не защита. Значит, обе болезни лечатся одновременно — ценой отказа от splice-ускорения.
Практическое воплощение — транспорт XHTTP + XMUX («XHTTP: Beyond REALITY», #4113) — VLESS поверх HTTP/2 или H3:
- число соединений лечит XMUX (объединяет логические запросы в одно физическое соединение) — ✅;
- форму TLS-в-TLS размывает (header-padding
xPaddingBytes+ разнесение upload/download + перемешивание потоков); - совместим с REALITY и работает за CDN — а это убирает заодно и Сигнал 1 (подсеть становится CDN’овской).
| Vision + REALITY (raw TCP) | XHTTP + XMUX (+ REALITY/CDN) | |
|---|---|---|
| TLS-в-TLS | ✅ padding + splice (сильнее по форме) | ⚠️ padding + split + mux (статистически, слабее) |
| Число соединений (Сигнал 3) | ❌ (mux нельзя) | ✅ XMUX |
| Подсеть (Сигнал 1) | ❌ | ✅ если за CDN |
| Скорость | ✅ splice | ❌ всё через userspace (медленнее) |
Честная оговорка
XHTTP прячет TLS-в-TLS слабее, чем кажется. По USENIX Security 2024 («Fingerprinting Obfuscated Proxy Traffic with Encapsulated TLS Handshakes») padding + мультиплексирование «многообещающи, но принципиально ограниченны»: они не уменьшают размер всплесков и число round-trip’ов, по которым вложенное рукопожатие и палится. XHTTP делает детект статистически дороже, а не невозможным. Почему именно — разобрано ниже, в пункте про round-trip’ы.
Итог: вылечить обе можно (XHTTP+XMUX), цена — потеря splice-скорости (а не принципиальный запрет) плюс «статистический», а не идеальный анти-TLS-в-TLS. Поэтому XTLS рекомендует держать оба профиля на сервере: Vision+REALITY+TCP для скорости/прямого подключения и XHTTP+XMUX для стелса/CDN (популярная «5-в-1» конфигурация).
А kTLS — вернёт ли он потерянную скорость?
kTLS (Kernel TLS) — функция ядра Linux, переносящая шифрование/расшифровку TLS-записей в ядро. За счёт этого forwarding может работать с zero-copy (sendfile/splice) даже когда прокси сам терминирует TLS. В xray это обсуждается (discussion #4270, issue #2565) как способ получить splice-уровень производительности без XTLS Vision — в обычном TLS-проксировании.
Но важно, чем kTLS не является:
- Это только производительность, не анти-детект. Он ничего не прячет — ни форму TLS-в-TLS, ни число соединений.
- Он не отменяет дилемму mux↔splice: ядерное копирование всё равно несовместимо с мультиплексором, который оборачивает поток в свои фреймы.
- В xray это пока экспериментально / на стадии обсуждения, а не готовая кнопка.
Грубо: kTLS — рычаг «вернуть часть CPU и скорости», ортогональный детекту. На вопрос «как спрятаться» он не отвечает; он про «как сделать это дешевле по ресурсам».
Почему padding и mux в принципе не прячут TLS-в-TLS (детект по round-trip’ам)
Самый глубокий уровень — почему padding и мультиплексирование лишь полумеры против детекта TLS-в-TLS. (Важно: это отдельный вектор, не «сибирский» Сигнал 3 — число параллельных соединений mux по-прежнему уменьшает; речь о том, что форму вложенного рукопожатия полностью спрятать не удаётся.) Объясняет это USENIX Security 2024.
TLS-рукопожатие — это характерная хореография: всплеск пакетов от клиента → пауза в один RTT (round-trip) → ответный всплеск от сервера → снова пауза → и т.д. Когда такое рукопожатие идёт внутри туннеля, эта последовательность «всплеск — пауза RTT — всплеск» просвечивает наружу узнаваемым ритмом в начале соединения.
И вот суть, почему обфускация не спасает:
- padding меняет размеры пакетов — но не меняет число round-trip’ов и не убирает структуру всплесков. Ритм остаётся.
- mux перемешивает потоки — но при малой нагрузке (один-два активных потока) перемешивать нечего, и начальное рукопожатие сохраняет свой round-trip-узор.
Поэтому детектор смотрит не на байты, а на последовательность всплесков, их направления и паузы между ними — и опознаёт «внутри идёт TLS-рукопожатие». Это устойчиво к padding’у и частично к mux’у.
Цифры из той работы стоит приводить аккуратно, потому что их постоянно сравнивают между собой некорректно — они относятся к разным конфигурациям и разным моделям. Набивка у vmess поверх WebSocket и TLS снижает TPR (долю верно распознанных прокси-потоков) с 0,859 до 0,687, причём авторы отдельно оговаривают, что схема набивки у vmess узкая — 0–63 байта. Для XTLS-Vision модель переучили так, чтобы она опиралась на направления и порядок пакетов, а не на их индивидуальные размеры; у неё TPR 0,513, но при вчетверо большем допуске ложных срабатываний (0,199 % против 0,054 %). То есть «51 %» — это не «Vision сбил детект с 70 % до 51 %», а результат другой модели на другом пороге. Мультиплексирование в тех же измерениях эффективнее: у голого vmess объединение двух потоков роняет TPR с 0,771 до 0,225 — более чем на 70 %, — а vmess поверх WebSocket и TLS при восьми параллельных потоках опускается до 0,125. Ключевая оговорка: при одном активном прикладном потоке перемешивать нечего, и mux деградирует до уровня обычного соединения.
Что нужно для настоящей защиты
По выводам статьи — не дополнять трафик, а переформировывать его: уменьшать размер всплесков и число round-trip’ов внутри соединения. Готового решения, полностью закрывающего это, в современных клиентах пока нет. Отсюда и фундаментальность гонки: обфускация поднимает стоимость детекта, но не делает его невозможным.
Можно ли mux без head-of-line blocking?
Раз у mux есть болячка HoL (потеря одного пакета тормозит все потоки) — можно ли мультиплексировать без неё? Можно, и это уже сделано — но сначала о корне.
Откуда берётся HoL. TCP — это один упорядоченный надёжный байтовый поток. Mux поверх него гонит все логические потоки через этот единый поток. Потерялся пакет → TCP держит всё, что пришло после него, пока не дождётся повтора — даже данные других потоков, которые уже дошли целыми. Это неизбежно для любого mux поверх одного TCP: ни Mux.Cool, ни HTTP/2 не уйдут — они все сидят на одном TCP-потоке (HTTP/2 убрал HoL на уровне приложения, но не транспорта).
Выход — не использовать единый TCP-поток. Надо дать каждому логическому потоку свою независимую упорядоченность. Это умеет QUIC (= HTTP/3): работает поверх UDP и реализует надёжность/порядок на каждый поток отдельно. Потерянный пакет блокирует только свой поток — остальные едут дальше. Ради этого QUIC и придумали.
В xray это уже есть: XHTTP поддерживает H3-режим (QUIC) → XMUX поверх QUIC = мультиплексирование без TCP-HoL.
Подвохи — почему это не дефолт
- QUIC = UDP, а UDP легко душат. Цензор может троттлить/резать UDP или QUIC целиком (в РФ бывает) → откат на TCP, и HoL возвращается. Отсюда же
xudpProxyUDP443: reject(форсить откат QUIC на TCP).- QUIC не прячет детект. Round-trip’ы вложенного рукопожатия (см. пункт выше) просвечивают и через него — QUIC лечит производительность/HoL, а не обнаружимость формы.
- Цена и совместимость. QUIC — userspace, дороже по CPU; поддержка H3 у CDN/промежуточных узлов неровная.
Итог: HoL неустраним поверх одного TCP — это закон. Обойти = перейти на per-stream-транспорт (QUIC/H3, XHTTP умеет), но вы меняете «HoL + надёжный TCP» на «без HoL, но по UDP, который цензор может прибить» — и форму трафика это всё равно не лечит. Бесплатного обеда нет.
🎯 Главный вывод
Эпоха «поставил REALITY и забыл» закончилась. DPI перешёл от анализа содержимого пакета к анализу поведения соединения. Отсюда три следствия:
- Идеальная криптография больше не гарантирует невидимость — она необходима, но недостаточна.
- Решает не один параметр, а совокупность косвенных признаков (подсеть + фингерпринт + поведение).
- Устойчивость даёт не «волшебная галочка», а грамотный профиль целиком: правильная подсеть, лояльный фингерпринт (uTLS), сдержанный паттерн соединений (mux + разные SNI).
Гонка щита и меча перешла на уровень статистики и поведения. Выигрывает тот, чей трафик скучный — максимально похожий на обычного пользователя и снаружи, и внутри.
📚 См. также
- 🔗 Первоисточник: habr.com/ru/articles/1044396 — Пётр Осетров (@hyperion_cs)
- 🔗 uTLS — refraction-networking/utls
- 🔗 dpi-checkers — hyperion-cs/dpi-checkers
- 🔗 xray-core — XTLS/Xray-core
- 🔍 Парная заметка: Как DPI анализирует соединение: воронка проверок (от SYN до ML) — общий конвейер, в который вписана эта схема
- 🦎 Парная заметка: Адаптивная мимикрия: статистический морфинг трафика — концепт, как можно было бы обойти поведенческий сигнал
- 🕵️ Полевой кейс: Блокировка сайта по JA4-отпечатку браузера — «Сигнал 2» в дикой природе: сайт не открывается только в Chrome/Edge
- 💥 Побочный ущерб: Как ТСПУ ломает легитимные сайты (июнь 2026) — те же три сигнала, но бьющие по обычным сайтам на хостингах, а не по обходным средствам
- 📈 Прогноз: Новая волна блокировок VPN (лето–осень 2026) — эта июньская схема в контексте ряда волн 2026 года; что в прогнозе подтверждено, а что остаётся экспертной гипотезой без даты
- Что такое обход DPI: VPN, Tor, DPI
- premium — рабочие конфиги
- Список SNI для VLESS
- Защита VLESS на стороне localhost
- Уязвимость VLESS + SOCKS5