Каждая публикуемая заметка получила callout-шапку со ссылкой на свою страницу wiki.zapret.moe (на самой вики она вырезается транформером RemoveMirrorCallout, видна только на зеркале Obsidian Publish и в Forgejo) и SEO-поле description — 1–2 предложения для meta description обоих сайтов. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
26 KiB
| date | tags | aliases | link | description | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-06-07 |
|
|
https://gist.github.com/Flowseal/de630dd9d9ddaa86cc6bed9b473fae0c | Почему смена JA4 и ротация SNI против детекции MTProxy работают только на стороне клиента Telegram, а серверный прокси изменить их не может. |
[!mirror] Резервное зеркало Актуальная версия этой страницы — на основной вики: wiki.zapret.moe/mtproxy/ja4-sni-client-side
🪪 Кто может менять JA4/SNI и почему обход MTProxy — клиентский
[!info] О чём заметка Разбор архитектурного факта, который определяет всю борьбу с новой детекцией MTProto: TLS-почерк (JA4) и имя домена (SNI) задаёт клиент Telegram, а не сервер-прокси. Поэтому чистые способы обхода (смена JA4, ротация SNI) работают только на стороне клиента, до цензора. Серверные прокси (mtproto.zig, telemt, teleproxy) изменить их физически не могут.
[!warning] Статус данных Параметры детекции — наблюдения сообщества (реверс-инжиниринг, июнь 2026), а не опубликованная спецификация ТСПУ. Числа и условия читай как «по наблюдениям», у разных операторов они могут отличаться. Архитектурный вывод (ClientHello генерит клиент) — это свойство самого TLS, оно не зависит от наблюдений.
Термины (чтобы заметка читалась без контекста)
- MTProxy / FakeTLS — прокси для Telegram, маскирующий трафик под обычный HTTPS: первый пакет выглядит как TLS-рукопожатие к популярному сайту.
- ClientHello — самый первый, ещё не зашифрованный пакет TLS-рукопожатия, который шлёт клиент. В нём открытым текстом: список шифров, расширения и SNI (имя домена, к которому якобы идёт подключение).
- JA4 — отпечаток ClientHello. Формат
t13d1516h2_…_…: первая часть — метаданные (t=TLS-over-TCP,13=TLS 1.3,d=есть SNI,15=число шифров,16=число расширений,h2=ALPN) — она не хешируется; а две следующие — хеши шифров и расширений, отсортированных перед хешированием (GREASE при этом выкидывается). Сортировка делает JA4 устойчивым к перетасовке порядка, в отличие от старого JA3. По отпечатку DPI понимает, какая программа сгенерировала пакет. - SNI (Server Name Indication) — поле в ClientHello с именем домена.
- ТСПУ — Технические Средства Противодействия Угрозам, DPI-оборудование у российских операторов.
TL;DR
- Новая детекция (примерно с начала июня 2026, по сообщениям сообщества) блокирует соединение, когда совпадают три условия: почерк JA4 Telegram + один и тот же SNI + несколько ClientHello одновременно на один ip:port. Слом любого одного условия — обход.
- JA4 и SNI лежат в ClientHello, а его шлёт клиент Telegram. Цензор видит этот пакет по пути client→server. Серверный прокси получает его уже после цензора — изменить не может.
- Поэтому смена JA4 и ротация SNI делаются только клиентским relay (локально, до цензора) — как в тестовом relay Flowseal.
- Серверный прокси (mtproto.zig/telemt/teleproxy) может бить только по третьему условию — «залп на один ip:port» (pacing, разные порты/IP).
- На Zig это реализуемо — но как отдельный клиентский инструмент, не как серверный бинарь mtproto.zig.
Сама детекция (наблюдения, июнь 2026)
Блокировка включается, когда одновременно:
- ClientHello несёт JA4 Telegram —
t13d1516h2_8daaf6152771_d8a2da3f94cd(мимикрия под Chrome 134/macOS; этот почерк протух, разбор #30733 — в Zapret/mtproto/10-telemt-logs-dpi); - у нескольких соединений один и тот же SNI;
- они идут залпом на один ip:port (несколько ClientHello почти одновременно).
Это логическое «И» — сломай любое условие, и правило не срабатывает.
Что помогает (по тестам сообщества):
- смена JA4 (почерк перестаёт совпадать с известным Telegram);
- ротация SNI (нет «одинакового SNI» в залпе).
[!warning] SNI лучше брать резолвящийся (гипотеза по аналогии с REALITY) По наблюдениям для VLESS+REALITY цензор проверяет A-запись SNI (резолвится ли домен). Применяется ли такая же проверка к MTProto-FakeTLS — отдельно не подтверждено, так что это осторожная рекомендация, а не факт. Безопаснее ротировать по списку реальных резолвящихся доменов. Тонкость про случайные сабдомены: сабдомен резолвится, только если базовый домен имеет wildcard A-запись. В relay Flowseal режим
--unique-sniклеит рандом к--sni-base; по умолчанию база —www.cloudflare.com(конкретный хост, не wildcard-зона), поэтому<hex>.www.cloudflare.comне резолвится — автор позиционирует дефолт как строгий тест «каждый SNI различается», а не как резолвящийся вариант. Чтобы сабдомены резолвились, нужно дать--sni-baseсвой домен с wildcard (это прямо предусмотрено в gist). Если проверка A-записи к MTProto всё же применяется, дефолтный--unique-sni— как раз потенциально рискованный режим.
Это та же «И трёх условий», что и «сибирская» схема для VLESS
Детект MTProto — не отдельное изобретение, а частный случай той же философии ТСПУ, что описана для VLESS+REALITY в VLESS/dpi-tls-june-2026 (первоисточник — статья @hyperion_cs на Хабре).
[!example] На пальцах Цензор перестал «открывать чемоданы» (вскрывать шифр — бесполезно, крипта цела) и начал смотреть на манеру пассажира: откуда приехал, во что одет, как себя ведёт. Блокировка включается, только когда совпали несколько признаков сразу (логическое «И»). Сломай любой один — правило не сработает. Это верно и для VLESS, и для MTProto — меняются лишь конкретные «признаки».
Отображение сигналов одной схемы на другую:
| «Сибирская» схема (VLESS+REALITY) | Детект MTProto (июнь 2026) |
|---|---|
| Подсеть сервера в списке подозрительных | подсеть так же в игре (зарубежные ДЦ тоже в списке) |
Фингерпринт uTLS (массовый chrome) |
JA4 Telegram t13d1516h2_8daaf6152771_… (мимикрия под Chrome 134) |
| Частота к одному SNI (>3 конн. <~350-400 мс / 60 с) | залп ClientHello на один ip:port |
| Ключ агрегации частоты — SNI | одинаковый SNI в залпе |
[!note] Про «подсеть» в строке выше В первоисточнике сигнал подсети одинаков и для VLESS, и (по экстраполяции) для MTProxy: в список подозрительных попали и российские ДЦ (в статье поимённо — Selectel, Я.Облако, Cloud.ru), и зарубежные провайдеры (в статье — обобщённо «методы затрагивали только зарубежных»; по общему знанию это Hetzner/DigitalOcean/OVH, флагнуты ещё раньше). То есть «арендовать VPS за рубежом» само по себе подсеть не ослабляет — ослабляет лишь попадание в нефлагнутую подсеть (редкий чистый провайдер, residential, CDN) либо малый трафик. (Сам MTProxy в статье @hyperion_cs не разбирается — перенос на него сделан здесь по аналогии.)
В обоих случаях это «И»: сломай одно звено — обход. Поэтому серверные меры (pacing, разные порты/домены) бьют по «частоте/залпу», а чистый слом «фингерпринта» (JA4) остаётся #Почему JA4/SNI меняются только на стороне клиента.
[!important] Протухший пресет = сам по себе аномалия Развивая логику фингерпринта (тезис из VLESS/dpi-tls-june-2026, по данным Cloudflare Radar — не из статьи @hyperion_cs, там фингерпринты делятся по марке браузера): важна не только марка, но и свежесть пресета. К концу 2025 больше половины «человеческих» TLS-соединений несут post-quantum
key_share(X25519MLKEM768) — он стал дефолтом в Chrome 131 и Firefox 132. Пресет без него, выдающий себя за свежий браузер, аномален сам по себе. Это можно трактовать как частный случай беды Telegram из #30733: клиент шлёт почерк протухшего Chrome 134/macOS. (В самом #30733 проблема описана как устаревший отпечаток в целом, без явного указания на отсутствие ML-KEM.) Лечится только обновлением почерка в клиенте (Zapret/mtproto/10-telemt-logs-dpi).
[!warning] «Ловушка для тех, кто дёргается» В «сибирской» схеме эскалация на ~600 с — это второй шаг, а не реакция на любую правку: сначала надо уже поймать первичную заморозку на 120 с (>3 параллельных конн. <~350-400 мс к одному SNI за 60 с), и только потом, если под этой заморозкой сменить фингерпринт, прилетает +600 с на весь TLS к узлу (независимо от почерка и SNI). Чистый перезапуск без залпа этого не вызывает. Вероятно, та же платформа ТСПУ обслуживает и MTProto, так что нервно крутить настройки под уже действующей блокировкой вредно: лучше переждать окно или сменить узел. (Точные тайминги и применимость к MTProto отдельно не подтверждены — это перенос логики со смежной схемы.)
Почему JA4/SNI меняются только на стороне клиента
[!example] На пальцах ClientHello — это визитка, которую показывает сам гость (приложение Telegram) на входе. Охранник (цензор ТСПУ) рассматривает визитку по дороге. Сервер-вышибала (прокси) получает гостя уже после охранника — переклеить визитку он не может, её давно увидели.
Telegram → [генерит ClientHello: JA4+SNI] → 👁 ЦЕНЗОР видит почерк → СЕРВЕР-прокси
▲
сюда пакет приходит уже после цензора
ClientHello — первый пакет рукопожатия, клиент строит и отправляет его до любого ответа сервера. Значит JA4 и SNI определены приложением Telegram.
Серверный прокси не может задним числом переписать уже ушедший пакет.
Единственный способ изменить то, что видит цензор, — встать между Telegram и цензором, то есть на устройстве пользователя (локальный relay) или в его локальной сети. Именно поэтому тестовый relay из gist — локальный: Telegramконнектится к нему на localhost, relay строит свой свежий ClientHello (с ротацией SNI и новым JA4) и уже его отправляет наружу.
| Действие | Где возможно | Серверный прокси |
|---|---|---|
| Сменить JA4 ClientHello | только client-side (до цензора) | ❌ невозможно |
| Ротировать SNI на каждое соединение | только client-side (SNI зашит в ссылке рядом с ee-секретом = tls_domain прокси, не выводится из секрета) |
❌ один неизменный tls_domain |
| Сломать «залп на один ip:port» | можно и на сервере | ⚠️ частично |
Что МОЖЕТ серверный прокси против этой детекции
Только третье условие — «несколько ClientHello одновременно на один ip:port»:
- Лимит SYN-ACK / pacing — растягивает «залп» по времени (разбор и per-port вариант — в Zapret/mtproto/10-telemt-logs-dpi).
- Разные порты / IPv6-hopping — размывает «один ip:port».
- Разные домены разным юзерам — размывает «один SNI» между пользователями (но это не ротация на одного юзера — у каждого
ee-секрет фиксирует один SNI).
Это частичные меры: детект — «И» трёх условий, так что слом даже одного помогает.
Но чистый слом (JA4/SNI) серверу недоступен.
Можно ли на Zig
Да — но как отдельный клиентский relay, а не как серверный mtproto.zig. Zig для этого подходит: статический бинарь, лёгкая кросс-компиляция под Windows/Linux.
Такой relay должен:
- слушать
localhost, принимать соединение от клиента Telegram; - на каждое соединение строить свежий FakeTLS-ClientHello: GREASE, ML-KEM key_share (post-quantum — заодно лечит протухший почерк #30733), тасовка расширений, HMAC секрета в
client_random; - ротировать SNI из списка реальных доменов (см. оговорку про A-запись выше);
- форвардить на апстрим-MTProxy.
[!note] Переиспользуемый код в mtproto.zig В проекте mtproto.zig уже есть TLS-обвязка (
src/protocol/tls.zig, генерация обфусцированного хендшейкаe2e_obf_handshake_gen.zig) — из неё можно собрать такой клиентский relay. Но серверный бинарь применить это к входящему трафику не может — см. раздел выше про сторону клиента.
[!tip] Тот же принцип уже воплощён рядом — в VLESS Свой Zig-relay — не единственное воплощение идеи «почерк генерит клиент». Для VLESS уже есть инструменты, которые вместо имитации берут подлинный сетевой стек браузера (NaiveProxy на cronet) или вообще реально установленный браузер (XHTTP + Browser Dialer в Xray) — почерк там аутентичный и свежий, без uTLS-попугайства.
⚠️ Но это другой протокол-стек: они строят браузерный HTTPS/VLESS-ClientHello, а не FakeTLS-MTProto (нет HMAC секрета прокси в
client_random), поэтому к MTProxy as-is не подключаются. Ценен здесь сам подход (подлинный почерк со стороны клиента), а не готовый MTProxy-клиент. Разбор и сравнение — VLESS/dpi-tls-june-2026#Альтернатива uTLS-пресетам: реальный стек Chromium (naive/cronet).
Миф: «в teleproxy JA4 решён»
teleproxy — серверный прокси (язык C), запускается на VPS, без клиентского компонента: пользователи подключаются к нему обычным клиентом по ссылке/QR. В его README речь идёт о статичном Chrome-профиле (517-байтный ClientHello, 15 расширений, GREASE, X25519, padding) на уровне JA3-имитации — ни статической, ни динамической работы именно с JA4, ни ротации SNI там нет. Есть только FakeTLS-камуфляж и форвард неизвестного SNI на реальный бэкенд (защита от активного зондирования).
Вывод: по архитектуре и собственной документации teleproxy не меняет и не ротирует входящий JA4 официального клиента Telegram — как и любой серверный прокси.
[!quote] Из доков самого teleproxy (
docs/features/dpi-resistance.md) «The primary detection vector is the Telegram client's TLS fingerprint, which cannot be fixed server-side… The Telegram app controls the byte-for-byte content of the ClientHello. Server-side proxy code cannot alter what the client puts on the wire.»
Почему конфиг teleproxy всё же «работает» — и это НЕ смена JA4
Три причины, ни одна из которых не меняет почерк:
- Фрагментация ClientHello через MSS-clamp. teleproxy ставит
TCP_MAXSEG=256(src/net/net-events.c), чтобы клиент порезал ClientHello на несколько TCP-сегментов: ALPN и signature_algorithms (входы JA4) уезжают во 2-3 сегмент, и DPI, считающий JA4 из первого пакета, получает неверный хеш. Сам JA4 при этом не меняется — ТСПУ просто не может его извлечь. ⚠️ Против DPI с полной пересборкой TCP-потока это не спасёт (об этом прямо сказано в доках teleproxy). Это ровно тот же приём, что mtproxy/mtproto-zig-setup#Шаг 4. TCPMSS — дробление ClientHello (там MSS=88 — даже агрессивнее) и [[Zapret/mtproto/11-telemt-server-setup#Шаг 4. Конфиги инстансов|client_mss="tspu"в telemt]] (MSS=92). - Чистая зарубежная подсеть VPS + малый трафик — остальные условия детекта не складываются (см. «И трёх условий» выше).
SOCKS5_PROXY+DIRECT_MODE— маршрут исходящего трафика VPS→DC через Xray (egress), по обфусцированному MTProto-транспорту к дата-центрам (не TLS). Ко входящему ClientHello (где живёт JA4) отношения не имеет.
Итого «решение JA4 в teleproxy» = фрагментация + чистый IP + egress через Xray, а не смена почерка. Чистая смена/ротация JA4 и SNI остаётся клиентской задачей.
📚 См. также
- mtproxy/tdlib-obf-client-side-stealth — готовое клиентское воплощение этого тезиса: форк библиотеки клиента строит свежий браузерный ClientHello (PQ-профили) внутри себя, без отдельного relay
- mtproxy/tsrman-tg-android-faketls — то же воплощение в виде готового приложения (форк официального Telegram-Android): меняет JA4 на Firefox-подобный и разносит коннекты джиттером
- mtproxy/telegram-wss-transport — третий путь мимо спора о почерке: соединение уходит не на адрес датацентра, а на веб-релей Telegram по 443, где TLS-рукопожатие уже настоящее
- mtproxy/mtproto-zig — как устроен FakeTLS-прокси, почему почерк фиксирован и узнаваем
- mtproxy/faketls-relay-diagnosis — измеренные требования релеев к ClientHello (длина, метка времени, повторы) и как проверить их снаружи
- mtproxy/mtproto-zig-setup — серверные меры (TCPMSS, SYN-ACK, домен)
- Zapret/mtproto/10-telemt-logs-dpi — детект DPI по логам, per-port pacing
- Zapret/mtproto/11-telemt-server-setup —
client_mss="tspu"(MSS=92) и UFW rate-limit как практический пример этих мер - Zapret/mtproto/05-censorship
- VLESS/dpi-tls-june-2026 — та же логика «И трёх условий» и про uTLS/свежесть почерка
- 🔗 Тестовый клиентский relay (gist, Flowseal) — смена JA4 + ротация SNI
- 🔗 tdesktop#30733 — протухший фингерпринт клиента