Как протокол заимствует чужое рукопожатие, почему v1 и v2 были дырявыми, что именно в v3 нашёл Aparecium и где у этого подхода архитектурный потолок

Большинство прокси-протоколов пытаются выглядеть как HTTPS: генерируют свой сертификат, настраивают TLS, надеются что DPI не присмотрится слишком внимательно. ShadowTLS идёт другим путём — берёт чужое рукопожатие целиком, не подделывая ничего

Пока TLS-рукопожатие идёт, всё что видит анализатор трафика — честный ClientHello, честный ServerHello, честный сертификат настоящего сервера-донора. Всё что происходит после завершения рукопожатия — туннельный трафик. К тому моменту DPI должен был уже принять решение пропустить соединение.

Прежде чем читать дальше. ShadowTLS v3 на март 2026 года работает не везде и не всегда. В июне 2025-го инструмент Aparecium показал два воспроизводимых вектора детекции: фиксированная разница в длине ServerFinished и некорректная обработка NewSessionTicket от OpenSSL-серверов. Против провайдеров с базовой SNI-блокировкой без активного зондирования протокол вполне работает. Против ТСПУ с активным зондированием риск реальный. Статья описывает архитектуру и механизм работы, а в конце разбирает где и почему детекция срабатывает.

Три узла и как они работают

Между клиентом и прокси-сервером стоит дополнительный компонент: сам ShadowTLS.

При подключении ShadowTLS проксирует TLS-рукопожатие на настоящий сервер-донор. Клиент получает настоящий сертификат, подписанный настоящим CA, — именно поэтому активное зондирование от DPI-системы не срабатывает: зондировщик тоже получает настоящий ответ настоящего сервера.
После завершения рукопожатия соединение переключается на прокси. Это переключение происходит внутри уже установленного TCP-потока, который с точки зрения сети выглядит как продолжение TLS-сессии с банком.

Как менялась аутентификация: v1, v2, v3

V1

Не имел защиты от активного зондирования вообще. Протокол просто проксировал TLS-рукопожатие на донора, после чего переключал поток на shadowsocks. Автор прямо описывал это допущение: v1 предполагал что man-in-the-middle смотрит только на рукопожатие и не делает активных проб. Допущение не выдержало первого же столкновения с реальностью.

V2

V2 добавил аутентификацию клиента через challenge-response. Challenge — весь трафик от сервера-донора за время рукопожатия (не только ServerRandom: так проще реализовать и меньше зависимость от деталей TLS-парсинга).

Клиент вычисляет HMAC-SHA1 над этими данными с использованием пре-шаред ключа и отправляет первые 8 байт результата в голове первого Application Data пакета.
Зондировщик без ключа не может угадать правильные 8 байт — соединение уходит на донора. Проблема целостности данных после рукопожатия осталась нерешённой.

V3

V3 переработал два слоя: аутентификацию клиента до рукопожатия и целостность данных после него.

SessionID как канал аутентификации. В TLS 1.3 поле SessionID в ClientHello не используется по назначению — браузеры заполняют его псевдослучайными байтами. ShadowTLS v3 размещает там подпись клиента:

SessionID[0:28] = random
SessionID[28:32] = HMAC(password, ClientHello)[0:4]

Сервер проверяет подпись при получении ClientHello. Если проверка не прошла — TCP-поток уходит к донору без изменений.

HMAC-цепочка для данных. Каждый фрейм после рукопожатия несёт 4-байтовую HMAC-подпись. Состояние обновляется после каждого фрейма — вставить или переиспользовать фрейм без знания пароля нельзя это закрывает атаку где man-in-the-middle берёт данные из одного направления и отправляет их обратно в другом — подписи не совпадут. После вычисления HMAC сам 4-байтовый тег дополнительно скармливается инстансу — это защищает от склейки двух соседних ApplicationData-фреймов в один.

Aparecium: что именно нашли в июне 2025-го

31 мая 2025 года появился инструмент Aparecium с описанием: «ShadowTLS v3 hereby declared dead due to its inherent design flaws» 1

За этим стоят две конкретные находки.

ServerFinished на 4 байта длиннее нормы. ShadowTLS v3 добавляет HMAC-тейнтинг к handshake-фреймам, включая ServerFinished. Стандартный TLS 1.3 ServerFinished от OpenSSL-сервера имеет длину 53 или 69 байт. Через ShadowTLS v3 он приходит длиной 57 или 73 байта

Стандарт OpenSSL:  ServerFinished = 53 bytes
Через ShadowTLS:   ServerFinished = 57 bytes

Aparecium сначала делает прямой запрос к серверу-донору и записывает длины его NewSessionTicket-сообщений. Потом подключается через ShadowTLS и сравнивает длины — если ServerFinished на 4 байта длиннее, протокол идентифицирован.

Неправильная обработка NewSessionTicket. В TLS 1.3 OpenSSL-серверы отправляют одно или два NewSessionTicket-сообщения после ClientFinished. ShadowTLS v3 прекращает релеить сообщения от донора после ClientFinished — и эти сообщения не доходят до клиента. Aparecium замечает их отсутствие, сравнивая с тем что приходит при прямом подключении к донору.

Оба метода детекции работают независимо друг от друга. Первый дает детерминированный результат, второй — вероятностный, но высокоточный.

Исключение. Go-based серверы (Google и его сервисы) отправляют NewSessionTicket до ClientFinished, а не после. ShadowTLS успевает его релеить — первый вектор детекции неактивен. Но второй (длина ServerFinished) остаётся.

Конфигурация

Для понимания архитектуры и использования там где продвинутый DPI отсутствует:

wget https://github.com/ihciah/shadow-tls/releases/latest/download/shadow-tls-x86\\_64-unknown-linux-musl
chmod +x shadow-tls-x86_64-unknown-linux-musl
mv shadow-tls-x86_64-unknown-linux-musl /usr/local/bin/shadow-tls

Сервер:

shadow-tls \
  --v3 \
  server \
  --listen 0.0.0.0:443 \
  --server 127.0.0.1:8388 \
  --tls addons.mozilla.org:443 \
  --password your-strong-password

Клиент:

shadow-tls \
  --v3 \
  client \
  --listen 127.0.0.1:1080 \
  --server your.server.ip:443 \
  --sni addons.mozilla.org \
  --password your-strong-password

Проверка донора на NewSessionTicket:

echo | openssl s_client -connect your-donor.com:443 -tls1_3 2>&1 | grep -i "session ticket"
addons.mozilla.org  - уязвим, ya.ru - не уязвим.
addons.mozilla.org - уязвим, ya.ru - не уязвим.

Если вывод пустой — донор не отправляет NST, первый вектор детекции неактивен. Второй (4 байта) остаётся в любом случае.

Доноры без NewSessionTicket:

*.google.com
*.googleapis.com
*.gstatic.com

Сравнение с Reality

У протоколов разная точка приложения усилий.

ShadowTLS проксирует рукопожатие через реального донора — клиент получает настоящий сертификат от настоящего CA и проверяет его стандартно.

Reality работает иначе: сервер проверяет shortId из поля SessionID в ClientHello. Если shortId совпал с конфигом — сервер генерирует сертификат на лету, подписанный x25519-ключом из конфига. Клиент проверяет его по pinned publicKey, а не по цепочке Apple или Microsoft. Если shortId не распознан сервер прозрачно проксирует TCP-поток на донора, зондировщик получает настоящий ответ и уходит.

Параметр

ShadowTLS v3

VLESS + Reality

Сертификат донора

Настоящий, цепочка CA

Генерируется на лету, pinned key

Нужен Xray

Нет

Да

Нужен домен

Нет

Нет

ServerFinished аномалия

+4 байта, детектируется

Нет

NST обработка

Уязвима (OpenSSL-доноры)

Частично уязвима

Сложность настройки

Низкая

Средняя

Актуальность против ТСПУ

Под вопросом

Выше

Где ShadowTLS имеет смысл

Против продвинутого DPI с активным зондированием — риск. Aparecium это proof-of-concept, не промышленная система, но вектор детекции описан точно и воспроизводим.

Против провайдерских блокировок по SNI без активного зондирования — работает. Для быстрой настройки без Xray и сложных конфигов — разумный выбор.

Архитектурный потолок

HMAC-тейнтинг добавляет 4 байта к ServerFinished. Убрать их нельзя, не сломав механизм аутентификации. Подделать NewSessionTicket-сообщения от OpenSSL-доноров после ClientFinished — технически решаемо, авторы Aparecium сами описали как: сервер должен отправлять сгенерированные NST нужной длины вместо отсутствующих.

Первое ограничение архитектурное, второе — реализационное. Патч для второго возможен, патч для первого потребует переработки всей схемы аутентификации.

На начало 2026 года обновлённой версии с исправлениями нет и последний коммит был 11 месяцев назад.

1. ban6cat6/aparecium: Aparecium is a proof-of-concept tool designed to detect TLS camouflage protocols
2. ihciah/shadow-tls: A proxy to expose real tls handshake to the firewall
3. ShadowTLS: A Better TLS Camouflage Proxy | Next Stop - Ihcblog!

Если есть идеи для разбора, нашёл ошибку
или хочешь предложить тему — пиши на
aleksandr@murzin.digital Отвечаю.