Some checks failed
Published content check / validate (push) Failing after 5s
Каждая публикуемая заметка получила callout-шапку со ссылкой на свою страницу wiki.zapret.moe (на самой вики она вырезается транформером RemoveMirrorCallout, видна только на зеркале Obsidian Publish и в Forgejo) и SEO-поле description — 1–2 предложения для meta description обоих сайтов. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
194 lines
26 KiB
Markdown
194 lines
26 KiB
Markdown
---
|
||
date: 2026-06-07
|
||
tags:
|
||
- mtproto
|
||
- mtproxy
|
||
- dpi
|
||
- tspu
|
||
- ja4
|
||
- sni
|
||
- faketls
|
||
aliases:
|
||
- Кто может менять JA4 и SNI
|
||
- Почему обход MTProxy клиентский
|
||
- JA4/SNI client-side
|
||
- Детекция MTProto июнь 2026
|
||
link: https://gist.github.com/Flowseal/de630dd9d9ddaa86cc6bed9b473fae0c
|
||
description: "Почему смена JA4 и ротация SNI против детекции MTProxy работают только на стороне клиента Telegram, а серверный прокси изменить их не может."
|
||
---
|
||
|
||
> [!mirror] Резервное зеркало
|
||
> Актуальная версия этой страницы — на основной вики: [wiki.zapret.moe/mtproxy/ja4-sni-client-side](https://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
|
||
|
||
1. Новая детекция (примерно с **начала июня 2026**, по сообщениям сообщества) блокирует соединение, когда совпадают **три** условия: почерк **JA4 Telegram** + **один и тот же SNI** + **несколько ClientHello одновременно на один ip:port**. Слом любого одного условия — обход.
|
||
2. JA4 и SNI лежат в **ClientHello**, а его шлёт **клиент Telegram**. Цензор видит этот пакет по пути client→server. **Серверный прокси получает его уже после цензора — изменить не может.**
|
||
3. Поэтому смена JA4 и ротация SNI делаются **только клиентским relay** (локально, до цензора) — как в [тестовом relay Flowseal](https://gist.github.com/Flowseal/de630dd9d9ddaa86cc6bed9b473fae0c).
|
||
4. Серверный прокси (mtproto.zig/telemt/teleproxy) может бить только по третьему условию — «залп на один ip:port» (pacing, разные порты/IP).
|
||
5. На **Zig это реализуемо** — но как отдельный **клиентский** инструмент, не как серверный бинарь mtproto.zig.
|
||
|
||
---
|
||
|
||
## Сама детекция (наблюдения, июнь 2026)
|
||
|
||
Блокировка включается, когда одновременно:
|
||
|
||
1. ClientHello несёт **JA4 Telegram** — `t13d1516h2_8daaf6152771_d8a2da3f94cd` (мимикрия под Chrome 134/macOS; этот почерк протух, разбор #30733 — в [[Zapret/mtproto/10-telemt-logs-dpi|10-telemt-logs-dpi]]);
|
||
2. у нескольких соединений **один и тот же SNI**;
|
||
3. они идут **залпом на один ip:port** (несколько ClientHello почти одновременно).
|
||
|
||
Это логическое «И» — сломай любое условие, и правило не срабатывает.
|
||
|
||
Что помогает (по тестам сообщества):
|
||
- **смена JA4** (почерк перестаёт совпадать с известным Telegram);
|
||
- **ротация SNI** (нет «одинакового SNI» в залпе).
|
||
|
||
> [!warning] SNI лучше брать резолвящийся (гипотеза по аналогии с REALITY)
|
||
> По наблюдениям для VLESS+REALITY цензор проверяет **A-запись** SNI (резолвится ли домен). Применяется ли такая же проверка к MTProto-FakeTLS — **отдельно не подтверждено**, так что это осторожная рекомендация, а не факт. Безопаснее ротировать по списку **реальных резолвящихся доменов**. Тонкость про случайные сабдомены: сабдомен резолвится, только если базовый домен имеет **wildcard A-запись**. В [relay Flowseal](https://gist.github.com/Flowseal/de630dd9d9ddaa86cc6bed9b473fae0c) режим `--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 на Хабре](https://habr.com/ru/articles/1044396/)).
|
||
|
||
> [!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|разбор #30733]]).
|
||
|
||
> [!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|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
|
||
|
||
Три причины, ни одна из которых не меняет почерк:
|
||
|
||
1. **Фрагментация 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|TCPMSS в mtproto.zig]] (там MSS=88 — даже агрессивнее) и [[Zapret/mtproto/11-telemt-server-setup#Шаг 4. Конфиги инстансов|`client_mss="tspu"` в telemt]] (MSS=92).
|
||
2. **Чистая зарубежная подсеть VPS** + малый трафик — остальные условия детекта не складываются (см. «И трёх условий» выше).
|
||
3. **`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|tdlib-obf — клиентский TDLib с маскировкой JA4]] — готовое клиентское воплощение этого тезиса: форк библиотеки клиента строит свежий браузерный ClientHello (PQ-профили) внутри себя, без отдельного relay
|
||
- [[mtproxy/tsrman-tg-android-faketls|tsrman/tg — Telegram для Android со сменой JA4]] — то же воплощение в виде **готового приложения** (форк официального Telegram-Android): меняет JA4 на Firefox-подобный и разносит коннекты джиттером
|
||
- [[mtproxy/telegram-wss-transport|WSS: MTProto внутри WebSocket]] — третий путь мимо спора о почерке: соединение уходит не на адрес датацентра, а на веб-релей Telegram по 443, где TLS-рукопожатие уже настоящее
|
||
- [[mtproxy/mtproto-zig|MTProxy и mtproto.zig]] — как устроен FakeTLS-прокси, почему почерк фиксирован и узнаваем
|
||
- [[mtproxy/faketls-relay-diagnosis|Релей или клиент: диагностика FakeTLS]] — измеренные требования релеев к ClientHello (длина, метка времени, повторы) и как проверить их снаружи
|
||
- [[mtproxy/mtproto-zig-setup|Настройка mtproto.zig (runbook)]] — серверные меры (TCPMSS, SYN-ACK, домен)
|
||
- [[Zapret/mtproto/10-telemt-logs-dpi|Чтение логов и лимит SYN-ACK]] — детект DPI по логам, per-port pacing
|
||
- [[Zapret/mtproto/11-telemt-server-setup|telemt в продакшн]] — `client_mss="tspu"` (MSS=92) и UFW rate-limit как практический пример этих мер
|
||
- [[Zapret/mtproto/05-censorship|ТСПУ: каскад детекции MTProto]]
|
||
- [[VLESS/dpi-tls-june-2026|Сибирская схема: подсеть + фингерпринт + частота]] — та же логика «И трёх условий» и про uTLS/свежесть почерка
|
||
- 🔗 [Тестовый клиентский relay (gist, Flowseal)](https://gist.github.com/Flowseal/de630dd9d9ddaa86cc6bed9b473fae0c) — смена JA4 + ротация SNI
|
||
- 🔗 [tdesktop#30733](https://github.com/telegramdesktop/tdesktop/issues/30733) — протухший фингерпринт клиента
|