🔍 Как DPI анализирует соединение: воронка проверок (от SYN до ML)
Первоисточник
Эта заметка основана на статье Александра Мурзина (@cyberscoper) на Хабре: 👉 «Как работает DPI: разбор по слоям» — habr.com/ru/articles/1009560
Это заметка-«карта»: общий конвейер анализа ТСПУ от первого SYN-пакета до ML-классификации. Глубокий разбор одной его стадии (поведенческой схемы июня 2026) — в парной заметке Как DPI «замораживает» VLESS+REALITY. Вместе они дают и общую картину, и детальный срез.
Дисклеймер
Материал исследовательский и образовательный. Цель — собрать в одну картину то, по каким признакам и в каком порядке DPI разбирает соединение, чтобы было понятно, какой обходной приём какой слой закрывает.
Важно про статус этих данных
Описанная «воронка» — это аналитическая модель, собранная по наблюдениям сообщества (ntc.party) и реверс-инжинирингу, а не опубликованная спецификация ТСПУ. Конкретные числа (5 пакетов, 83 байта, 16 КБ, 95–99 %, 1–5 мс) читайте как порядок величин и авторские оценки, а не как точные константы системы. Реальная реализация различается от оператора к оператору и меняется со временем.
TL;DR — анализ идёт «воронкой»
DPI не выносит вердикт по одному пакету. Он накапливает данные и отсеивает на каждом уровне: дешёвые проверки сначала, дорогой ML — в конце и только для выживших.
трафик
│
пакеты 1–5 ┌───▼───┐ TTL, TCP-опции (MSS/WS/SACK/TS) → отпечаток ОС
(TCP/IP) └───┬───┘
│
байты 0–5 ┌───▼───┐ протокольный маркер: 16 03 01… / SSH-2.0- / …
(сигнатура) └───┬───┘
│
байты 6–300 ┌───▼───┐ ClientHello → JA3 / JA4 (фингерпринт клиента)
(TLS hello) └───┬───┘
│
байты ┌───▼───┐ сертификат*: CT-логи, ASN, CN/SAN ↔ SNI
300–3000 └───┬───┘ (* в TLS 1.3 сертификат шифруется →
│ пассивно остаются только SNI и ASN)
байты ┌───▼───┐ метаанализ потока: размеры, тайминги,
3000–16000 └───┬───┘ соотношение in/out, длительность
│
после 16 КБ ┌───▼───┐ поведенческий ML-классификатор
└───┬───┘
│
вердикт → пропустить / RST / drop / throttleЧем дальше по воронке — тем дороже проверка и тем труднее её обмануть. Обходные средства бьются за то, чтобы отсеяться пораньше (выглядеть скучно уже на ранних слоях).
Как это связано с «сибирской» схемой
Поведенческая схема июня 2026 (подсеть + фингерпринт + частота) — это, по сути, нижние этажи этой воронки: фингерпринт = слой JA3/JA4, частота/параллелизм = слой метаанализа потока. Подробный разбор именно её — в dpi-tls-june-2026. Здесь — как она вписана в общий конвейер.
Этап 0. TCP/IP — ещё до всякого TLS
DPI начинает «нюхать» соединение раньше, чем дойдёт до шифрования — на уровне самих TCP/IP-заголовков.
TTL — отпечаток ОС и детектор fake-пакетов. Разные ОС ставят разный стартовый TTL:
- Windows → TTL 128, Linux → TTL 64 (macOS/iOS тоже 64).
- Каждый промежуточный хоп уменьшает TTL на 1: пакет с
TTL 63прошёл один хоп,TTL 62— два.
Отсюда — два следствия. Во-первых, по TTL грубо угадывается ОС клиента. Во-вторых (важнее для обхода): на TTL ловятся fake-пакеты инструментов вроде GoodbyeDPI — те шлют поддельный пакет с маленьким TTL, чтобы он дошёл до DPI, но «умер» по дороге к серверу. Если DPI умеет сверять TTL, он отличает такой fake от настоящего пакета. Это прямая связь нашей «воронки» с миром Zapret.
TCP-опции — пассивный отпечаток ОС (p0f). Набор и порядок TCP-опций в SYN-пакете — MSS, Window Scale, SACK, Timestamps — различается между операционными системами и их версиями. Это классический пассивный fingerprinting (техника p0f): по одному только SYN можно сказать «это похоже на Windows 11» или «это похоже на Linux».
Авторская оценка (под оговорку)
«Первые пять пакетов соединения проходят без активного вмешательства» — DPI сначала собирает контекст. И: «если первый содержательный пакет после рукопожатия короче 83 байт — подозрительно». Эти числа — иллюстрация логики, а не подтверждённые константы.
Этап 1. Протокольный маркер — первые байты данных
Как только пошли данные, DPI смотрит на первые байты — у многих протоколов начало узнаваемо:
| Протокол | Маркер начала |
|---|---|
| TLS ClientHello | 16 03 01 xx xx 01 00 xx xx xx 03 03 … |
| SSH | ASCII-строка SSH-2.0-… |
| OpenVPN | свой характерный заголовок |
| WireGuard | характерный UDP-заголовок (тип сообщения) |
| Shadowsocks (без обфускации) | статистически случайные байты — отсутствие маркера само по себе примета |
Разбор TLS-маркера: 16 = Content Type Handshake; 03 01 = версия record-слоя (легаси, всегда «TLS 1.0» ради совместимости); 01 = Handshake Type ClientHello; 03 03 = legacy_version.
Поправка к первоисточнику
Источник пишет, что
03 03— это «TLS 1.3 внутри ClientHello». Это неточность:0x0303— этоlegacy_version(формально TLS 1.2), поставленный ради совместимости. Настоящая версия TLS 1.3 объявляется не здесь, а в расширенииsupported_versions(0x0304). Полный побайтовый разбор реальногоClientHello— в байт-дампе RFC 8448 из парной заметки.
Любопытно, что Shadowsocks проходит этот этап «наоборот»: у него нет маркера вообще, поток выглядит как энтропийный шум. На фоне веба, где почти всё — TLS с узнаваемым началом, «совершенно случайные байты с первого пакета» — сами по себе аномалия (по этому признаку, кстати, и ловят многие обфусцированные протоколы).
Этап 2. JA3 / JA4 — отпечаток клиента по ClientHello
Если маркер сказал «это TLS», DPI сворачивает ClientHello в фингерпринт. Точное определение JA3:
JA3 = MD5(
SSLVersion,
Ciphers, # список шифров, без GREASE-значений
Extensions, # список расширений, без GREASE-значений
EllipticCurves,
EllipticCurvePointFormats
)JA4 (FoxIO, 2023) — новее и устойчивее: менее чувствителен к перетасовке порядка расширений (сортирует их), разделяет клиентскую и серверную части, даёт более стабильный отпечаток.
По этому слою DPI говорит «это Chrome 13x» или «это голый Go-клиент». Здесь же и кроется ловушка массового fingerprint: chrome. Глубокий разбор — что такое фингерпринт побайтово, чем JA3 отличается от JA4, почему «голый» Go палится и что делает uTLS — в разделе про uTLS парной заметки.
Нюанс про GREASE в JA3
То, что JA3 исключает GREASE-значения, — палка о двух концах. С одной стороны, отпечаток не «плавает» из-за случайных GREASE. С другой — Chrome тасует порядок расширений на каждом соединении (официально с Chrome 110, фактически проявлялось уже в сборках 108–109, начало 2023), а порядок в JA3 учитывается → у Chrome JA3 всё равно «плавает». Именно поэтому и появился JA4 с сортировкой.
Этап 3. Сертификат — проверка TLS-личности сервера
Дальше DPI смотрит на ServerHello и сертификат сервера и сверяет три вещи:
- Certificate Transparency. Существует ли такой сертификат в публичных CT-логах? Выпущен ли он реально на этот домен?
- ASN сервера. IP-адрес должен лежать в «правильной» автономной системе для этого бренда. Apple держит серверы в AS714, Microsoft — в AS8075. Если SNI говорит
icloud.com, а IP — из чужой AS, это рассогласование. - SNI ↔ CN/SAN. Имя в
ClientHello(SNI) должно совпадать с именем в сертификате. Если SNI =icloud.com, а приехал сертификат Let’s Encrypt на случайный домен — не совпало.
Главная оговорка этапа: в TLS 1.3 сертификат не виден
Две из трёх проверок выше пассивно работают в основном на TLS 1.2. В TLS 1.3 сообщение
Certificateидёт зашифрованным (послеServerHello, под ключами рукопожатия) — пассивный наблюдатель его не читает (RFC 8446). А сегодня почти весь веб — это TLS 1.3. Поэтому:
- CT-логи и сверка CN/SAN ↔ SNI для пассивного DPI на TLS 1.3 фактически недоступны — для них нужен активный MITM/перехват (это уже не «пассивная воронка»). На TLS 1.2 — работают.
- Надёжно пассивно в TLS 1.3 виден только SNI в
ClientHello(если не включён ECH) и IP/ASN назначения.- «Бренд = одна AS» — упрощение: Apple/Microsoft/Google массово раздают трафик через сторонние CDN, и тогда IP легитимно лежит в чужой AS (напр., egress-узлы iCloud Private Relay идут через сторонние CDN — Akamai, Cloudflare, Fastly, — то есть не из AS714). Грубая сверка «чужая AS → палево» даёт ложные срабатывания; реальный DPI держит allow-list всех AS/CDN бренда.
Почему REALITY проходит этот этап
Это ровно тот слой, который закрывает REALITY — и работает он только поверх TLS 1.3, что здесь принципиально. REALITY не выпускает свой сертификат: сервер ретранслирует рукопожатие клиента к реальному
dest(популярному сайту). Дальше важно различать двух адресатов:
- Для постороннего наблюдателя/DPI соединение неотличимо от настоящего TLS к
dest: правильные SNI, IP и ASN, а самCertificate— зашифрован (TLS 1.3), так что наблюдатель его и не читает. Именно на шифровании сертификата и держится незаметность — DPI нечего сверять с CT.- Для «своего» клиента REALITY-сервер на лету подменяет сертификат на временный доверенный (привязан к ECDH-секрету/AuthKey) — по нему клиент опознаёт «свой» сервер. Если же клиенту приходит настоящий сертификат
dest, это сигнал «меня редиректят/MITM-ят», и клиент уходит в режим обычного веб-браузера.Классический же VLESS/Trojan со своим сертификатом слабее — но пассивно в TLS 1.3 его выдаёт не чтение сертификата, а IP/ASN хостинга и SNI на домен без трафика и репутации; сам Let’s Encrypt-сертификат виден лишь при активном зондировании или на TLS 1.2. Подробнее про заимствование личности — в парной заметке.
Этап 4. Метаанализ потока — поведение, а не содержимое
Если по сертификату не придрались, DPI переходит к форме трафика — её шифрование не скрывает. Здесь смотрят на статистику:
- Соотношение in/out. У браузера трафик асимметричен: маленькие запросы, большие ответы — типично 1:10 … 1:20. Туннель, по которому льётся всё подряд, это соотношение нарушает.
- Межпакетные интервалы. Браузер делает паузы (парсинг, отрисовка, реакция человека) — десятки–сотни миллисекунд. Туннель часто гонит данные ровным потоком без «человеческих» пауз.
- Длительность соединения. Браузер живёт минутами (keep-alive), потом закрывается. VLESS держит соединение открытым часами — это аномалия.
Это и есть тот уровень, на котором работает Сигнал 3 «сибирской» схемы (частота и параллелизм соединений к одному SNI). Разбор поведенческого сигнала и лекарства от него (mux, разные SNI) — в парной заметке.
Этап 5. ML-классификация — после ~16 КБ
Для соединений, доживших до этого этапа, DPI скармливает накопленную статистику (размеры пакетов, тайминги, энтропию, ритм всплесков) ML-классификатору.
Заявленная точность (под оговорку)
«Точность ML-классификаторов в лабораторных условиях достигает 95–99 % для Shadowsocks и VMess. В продакшне — ниже». Лабораторные числа всегда оптимистичны: в реальном трафике с шумом, ложными срабатываниями и ценой ошибки порог срабатывания держат заметно выше.
Не путать «16 КБ → ML» и метод «tcp 16-20»
Совпадение порога обманчиво. В dpi-tls-june-2026 упоминается отдельный метод «tcp 16-20» (l4-25) — он тупо замораживает соединение после ~16 КБ / ~25 пакетов, без всякого ML. Здесь же 16 КБ — это момент, когда у DPI накопилось достаточно данных, чтобы запустить статистику. Это два разных механизма с похожим порогом, а не одно и то же.
Связь с фундаментальным пределом обфускации: даже padding и mux не убирают round-trip-узор вложенного TLS-рукопожатия (детект по USENIX Security 2024). Почему именно — разобрано в парной заметке.
Что DPI делает с вердиктом
Опознав обход, DPI применяет одну из трёх мер:
| Метод | Что делает | Как ощущается |
|---|---|---|
| TCP RST injection | вбрасывает поддельный RST в поток | соединение резко рвётся |
| Drop пакетов | молча не пропускает пакеты | «зависание», потом таймаут |
| Throttling | намеренное замедление + потери | «как будто плохой интернет» |
«Заморозка на 120/600 секунд» из «сибирской» схемы — это вариант throttling: соединение не рвут демонстративно, а тихо душат, чтобы было похоже на плохую связь, а не на цензуру.
Про задержку самого DPI (под оговорку)
Активная инспекция добавляет «1–5 мс для большинства соединений». Само по себе это иногда косвенный признак присутствия DPI на маршруте.
Контрмеры по слоям: кто какой этаж закрывает
Главный практический вывод обеих заметок совпадает: ни один инструмент в одиночку не закрывает все слои. Каждый приём бьёт по своему этажу воронки:
| Слой воронки | Чем закрывается |
|---|---|
| TCP/IP (TTL, опции ОС) | корректный стек ОС; для Zapret — аккуратные fake с верным TTL |
| Протокольный маркер (байты 0–5) | TLS-обёртка (REALITY/Trojan) — выглядит как обычный TLS |
| JA3 / JA4 (байты 6–300) | uTLS-фингерпринт (fingerprint: firefox/edge/…) |
| SNI / ASN / сертификат (300–3000)¹ | REALITY (правильный SNI-донор + чужой ASN + защита от активного зондирования) |
| Метаанализ потока (3000–16 000) | mux / разнесение по SNI / xPaddingBytes |
| ML (после 16 КБ) | нормализация формы (XHTTP+XMUX) — и то лишь статистически |
¹ В TLS 1.3 сам сертификат пассивно не виден (зашифрован), поэтому пассивно на этом этаже остаются SNI и ASN/IP; REALITY закрывает их (чужой знаменитый SNI-донор + правильный ASN) и защищает от активного зондирования — а не «прячет чтение сертификата», которого пассивный DPI в TLS 1.3 и так не делает (см. callout в Этапе 3).
Видно, что слои ортогональны: можно идеально закрыть JA3 и провалиться по подсети; можно спрятать сертификат через REALITY и спалиться по поведению потока. Устойчивость даёт не «волшебная галочка», а закрытие воронки сверху донизу — ровно к этому и приходит разбор в парной заметке.
⚠️ Честная оговорка про числа
Чтобы не выдавать модель за спецификацию — что здесь проверяемый факт, а что авторская оценка:
- ✅ Твёрдо: TTL 128/64 и декремент по хопам; TCP-опции как отпечаток ОС (p0f); сигнатура
16 03 01…;SSH-2.0-; формула JA3 (5 полей, без GREASE); JA4/FoxIO 2023 (сортировка шифров и расширений); ASN-факты (AS714 Apple, AS8075 Microsoft); методы RST/drop/throttle. - ⚠️ Иллюстративно / под оговорку: «5 пакетов без вмешательства», «короче 83 байт», пороги байтовых стадий (0–5 / 6–300 / …), «95–99 % в лаборатории», «1–5 мс задержки», соотношения 1:10–1:20. Это порядок величин и логика, а не подтверждённые константы ТСПУ.
- 🪧 С важной оговоркой: проверки CT-логов и CN/SAN ↔ SNI (Этап 3) — реальные техники, но пассивно доступны в основном на TLS 1.2; в TLS 1.3 сертификат зашифрован, и для них нужен активный MITM. Связка «бренд = одна AS» ломается на CDN.
- 🩹 Поправлено:
0x0303— этоlegacy_version(TLS 1.2), а не «TLS 1.3 внутри ClientHello» (настоящая версия — вsupported_versions); из формулы JA3 убрано ошибочное «без padding (21)» — каноничный JA3 исключает только GREASE.
📚 См. также
- 🔗 Первоисточник: habr.com/ru/articles/1009560 — Александр Мурзин (@cyberscoper)
- 🧊 Парная заметка: Как DPI «замораживает» VLESS+REALITY: схема июня 2026 — глубокий разбор поведенческой стадии
- 🦎 Парная заметка: Адаптивная мимикрия: статистический морфинг трафика — концепт-ответ на поведенческий детект (Этап 4)
- 🕵️ Полевой кейс: Блокировка сайта по JA4-отпечатку браузера — как слой JA3/JA4 этой воронки бьёт по обычному Chrome
- 🔗 JA4 — FoxIO-LLC/ja4
- 🔗 USENIX Security 2024 — Fingerprinting Obfuscated Proxy Traffic (round-trip-узор вложенного TLS — Этап 5)
- 🔗 USENIX Security 2023 — How the GFW Detects and Blocks Fully Encrypted Traffic (Wu et al.) (детект «бесмаркерного» трафика по энтропии — Этап 1)
- 🔗 IMC 2020 — How China Detects and Blocks Shadowsocks (энтропия первого пакета + активное зондирование — Этапы 1 и 5)
- Что такое обход DPI: VPN, Tor, DPI
- Список SNI для VLESS