Как протокол заимствует чужое рукопожатие, почему 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"

Если вывод пустой — донор не отправляет 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 Отвечаю.
