Навигация по курсу: Часть 4
Глубокие разборы протоколов. Вы читаете часть 4:
Deep Dive: XHTTP
Эта статья посвящена детальному разбору протокола XHTTP. Для понимания контекста рекомендуем сначала прочитать про Алгоритмы блокировок ТСПУ.
Введение: Смена парадигмы маскировки
К концу 2024 года команда XTLS/Xray-core представила XHTTP — революционный транспортный протокол, который фундаментально переосмысливает подход к передаче зашифрованного трафика. В отличие от своих предшественников (VMess, VLESS over TCP/WS), которые инкапсулировали прокси-протоколы внутри TLS, создавая детектируемые сигнатуры «TLS-в-TLS», XHTTP оперирует нативными HTTP-запросами.
Проблема вложенности (TLS-in-TLS) и статических паттернов подробно разобрана в нашей статье про Сетевые фундаменты. XHTTP решает её, отказываясь от длинных туннелей.
С точки зрения сети, трафик XHTTP неотличим от обычной работы с Content Delivery Network (CDN). Это архитектурное различие дает колоссальное преимущество как против классических систем DPI (Deep Packet Inspection), так и против новейших анализаторов на базе машинного обучения (ML).
В этом исследовании мы детально разберем три режима работы XHTTP, механизмы рандомизации XMUX и стратегию разделения потоков (Download-Upload Separation).
Часть 1. Три режима работы XHTTP
XHTTP реализует три функционально различных режима передачи данных. Критически важно, что эти режимы полностью отделены от версии HTTP (HTTP/1.1, HTTP/2, HTTP/3), что позволяет создавать гибкие комбинации под конкретные сетевые условия.
1. Packet-UP: Универсальная совместимость
Этот режим разработан как «безотказный вариант» (fallback). Он имитирует классическую загрузку файлов частями и работу с длинными HTTP-запросами, что позволяет ему проходить сквозь любые CDN и корпоративные фаерволы.
Архитектура:
- Uplink (Клиент → Сервер): Серия последовательных POST-запросов. Каждый запрос несет в себе порядковый номер (Sequence Number).
POST /yourpath/[random-UUID]/[seq=0,1,2,3...] Body: 1MB data chunk - Downlink (Сервер → Клиент): Единственный долгоживущий GET-запрос с потоковой передачей ответа.
GET /yourpath/[random-UUID] Response: Chunked transfer encoding (бесконечный поток данных)
Технические детали: Сервер собирает POST-запроса в буфер (по умолчанию до 30 пакетов) и восстанавливает порядок данных на основе Sequence Number. Это критично, так как CDN могут менять порядок доставки пакетов.
Для маскировки каждый POST-запрос содержит заголовок Referer с рандомизированным заполнением (x_padding=XXX), размер которого варьируется от 100 до 1000 байт. Ответ сервера (Downlink) содержит специальные заголовки для предотвращения кэширования промежуточными узлами:
| Заголовок | Назначение |
|---|---|
X-Accel-Buffering: no | Отключает буферизацию на стороне Nginx/CDN |
Cache-Control: no-store | Запрещает сохранение данных в кэш |
Content-Type: text/event-stream | Маскировка под Server-Sent Events (SSE) |
Transfer-Encoding: chunked | Включает потоковую передачу для HTTP/1.1 |
Параметры производительности (настраиваемые):
scMaxEachPostBytes: 500 КБ – 1 МБ (размер одного POST).scMinPostsIntervalMs: 10–50 мс (интервал между POST-запросами для размытия паттерна).
2. Stream-UP: Оптимизация под HTTP/2
Режим реализует настоящий двунаправленный стриминг, устраняя накладные расходы на создание множества POST-запросов. Требует поддержки HTTP/2.
Архитектура:
- Uplink: Один бесконечный POST-запрос с потоковой передачей тела.
POST /yourpath/[random-UUID] Headers: Referer с паддингом, Content-Type: application/grpc Body: Поток данных (без sequence numbers) - Downlink: Отдельный стриминговый GET-запрос.
Маскировка под gRPC:
По умолчанию Stream-UP добавляет заголовки Content-Type: application/grpc и TE: trailers. Это заставляет промежуточные системы (особенно Cloudflare) обрабатывать трафик как легитимный gRPC — протокол, который разрешен большинством провайдеров для бизнес-задач. Это «косметическая» маскировка: внутри не передается настоящий gRPC, но для CDN трафик выглядит именно так.
Проблема таймаутов Cloudflare:
CDN часто разрывают соединения, если в них нет данных более 100 секунд. Stream-UP решает это параметром scStreamUpServerSecs (по умолчанию 20–80 секунд): сервер отправляет маленькие пакеты-пустышки (keepalive padding), чтобы поддерживать канал активным.
3. Stream-ONE: Интеграция с REALITY
Специальный режим, сводящий всё взаимодействие к одному HTTP-запросу.
Архитектура:
POST /yourpath/
Request body: Поток данных Uplink
Response body: Поток данных Downlink
Это создает всего 1 HTTP-запрос на соединение (против 2 в Stream-UP и N в Packet-UP). Режим идеален для использования в связке с протоколом REALITY (XTLS-Vision), минимизируя количество рукопожатий, но не позволяет разделить потоки загрузки и выгрузки.
Часть 2. Независимость от версий HTTP и XMUX
Фундаментальный принцип XHTTP — полная отвязка логики транспорта от версии протокола HTTP.
Гибкость комбинаций
- Packet-UP over HTTP/2: Последовательные POST-запросы мультиплексируются внутри одного TCP-соединения как разные стримы.
- Stream-UP over HTTP/3 (QUIC): Использование QUIC позволяет менять сети (Wi-Fi ↔ LTE) без разрыва соединения (Connection Migration).
- Stream-ONE over HTTP/1.1: Работает поверх чистого TCP.
Серверная архитектура
Серверы XHTTP по умолчанию слушают только TCP (HTTP/1.1 и HTTP/2). Поддержка QUIC (HTTP/3) реализуется через Nginx/Caddy или CDN перед сервером, которые конвертируют QUIC-запросы от клиента в HTTP/1.1/2 для бэкенда.
XMUX: Интеллектуальное мультиплексирование
Система XMUX обеспечивает контроль над тем, как именно запросы распределяются по соединениям. Это главное оружие против фингерпринтинга.
Ключевые параметры рандомизации:
| Параметр | Значение (пример) | Назначение |
|---|---|---|
maxConcurrency | ”16-32” (случайно) | Количество одновременных стримов в одном TCP-соединении |
hMaxRequestTimes | ”600-900” (случайно) | Лимит запросов в одном соединении до его сброса |
hMaxReusableSecs | ”1800-3000” (случайно) | Время жизни соединения (около 30-50 минут) |
Зачем это нужно? Традиционные прокси (например, на базе gRPC) создают одно длинное соединение и гоняют в нем данные часами. Это создает статический паттерн. XMUX заставляет клиент постоянно менять поведение:
- Сегодня соединение живет 1900 секунд и держит 16 стримов.
- Завтра — 2800 секунд и 28 стримов. Это ломает ML-модели, обучающиеся на фиксированных паттернах поведения (Behavioral Analysis).
Часть 3. Сравнение с WebSocket, HTTP/2 и VMess
XHTTP против WebSocket
WebSocket имеет критическую уязвимость: фиксированный отпечаток ALPN (http/1.1). Даже внутри HTTP/2 туннеля браузеры не могут использовать нативный H2-фрейминг для WebSocket, требуя механизм Upgrade. XHTTP использует нативный HTTP/2 или HTTP/3, полностью сливаясь с трафиком современного веба.
XHTTP против VMess/VLESS+TLS
Здесь различие фундаментально.
- VMess/VLESS: Данные пользователя → Протокол VMess → Шифрование TLS. Это создает структуру «матрешки». Исследования (USENIX 2024) показали, что наличие вложенного рукопожатия (Inner Handshake) создает аномалии в таймингах (RTT Discrepancy) и порядке пакетов, которые видят ML-модели.
- XHTTP: Данные пользователя → Обычный HTTP-запрос (GET/POST) → TLS. Здесь нет вложенных протоколов. Для сети это выглядит как обычная загрузка файла на Google Drive или просмотр YouTube. Нет «второго дна», нет подозрительных таймингов.
Часть 4. Почему XHTTP невидим для ML-анализаторов
Современные DPI (ТСПУ) используют машинное обучение, анализируя гистограммы размеров пакетов и интервалы между ними. Как мы выяснили в разборе поведенческого анализа ТСПУ, нейросети ищут стабильные паттерны энтропии. Рандомизация XHTTP ломает эти метрики.
XHTTP разрушает эти метрики тремя слоями рандомизации:
- Padding Randomization: Каждый запрос содержит мусорные данные (100–1000 байт) в заголовке Referer. Один и тот же запрос данных на 1 КБ в сети будет выглядеть каждый раз по-разному: то 1234 байта, то 1589 байт.
- Volume Randomization: Параметр
scMinPostsIntervalMsвносит случайные задержки (джиттер) между отправкой пакетов в режиме Packet-UP, ломая временные паттерны. - Lifecycle Randomization: Благодаря XMUX, у соединения нет фиксированного времени жизни или объема переданных данных.
Проблема статистической сходимости: Чтобы ML-модель научилась детектировать протокол, ей нужно найти общие черты в тысячах соединений. XHTTP генерирует настолько вариативный трафик (разные размеры, разные тайминги, разные режимы), что пространство признаков становится слишком широким. Любая попытка заблокировать такой паттерн приведет к массовым ложным срабатываниям на легитимных сервисах (API, облачные хранилища).
Часть 5. Разделение загрузки и выгрузки (Download-Upload Separation)
Уникальная фишка XHTTP, недоступная в VLESS/Trojan. Вы можете настроить разные маршруты для входящего и исходящего трафика.
Пример конфигурации (асимметричный поток):
- Uplink (Исходящий): Идет через IPv4, протокол HTTP/2, сервер
cdn.example.com. - Downlink (Входящий): Идет через IPv6, протокол HTTP/3 (QUIC), сервер
ipv6-cdn.example.com.
Эффект для DPI: Анализатор видит два абсолютно несвязанных потока.
- Поток данных, уходящий на IPv4.
- Поток данных, приходящий с IPv6. Между ними нет корреляции по портам, IP или таймингам (благодаря разному XMUX). Для системы слежения это выглядит как два разных пользователя, занимающихся разными задачами. Связать их в одну сессию невозможно.
Часть 6. Известные проблемы и решения (Q4 2024)
Проблема ADSL и Back-pressure (Issue #4100)
В декабре 2024 года обнаружилось, что на медленных каналах (upload < 1 Mbps) режим Packet-UP зависал. Причина: клиент отправлял запросы быстрее, чем канал мог их пропустить, переполняя буфер сокета.
Решение: Внедрен механизм Back-pressure. Теперь клиент ждет подтверждения записи в буфер сокета перед отправкой следующего POST.
Временный фикс конфигом: Снижение scMaxEachPostBytes до 100-300 КБ помогает на очень плохих линиях.
Таймаут 100 секунд в Cloudflare
Если в канале Stream-UP нет данных, Cloudflare рвет соединение.
Решение: Параметр scStreamUpServerSecs заставляет сервер отправлять пустые фреймы, имитируя активность.
Часть 7. Рекомендуемые конфигурации
Конфигурация 1: Универсальная (Максимальная совместимость)
Использует режим auto (выбирает Packet-UP), работает через любые CDN.
{
"streamSettings": {
"network": "xhttp",
"xhttpSettings": {
"path": "/v1/",
"host": "cdn.example.com",
"mode": "auto"
},
"extra": {
"xPaddingBytes": "500-2000",
"xmux": {
"maxConcurrency": "8-64",
"hMaxRequestTimes": "200-1000"
}
}
}
}
Конфигурация 2: Скоростная с разделением потоков
Использует Stream-UP для скорости и разные IP для запутывания следов.
{
"streamSettings": {
"network": "xhttp",
"xhttpSettings": {
"path": "/api/v2/",
"host": "upload.example.com",
"mode": "stream-up"
},
"security": "tls",
"tlsSettings": { "alpn": ["h2"], "serverName": "upload.example.com" },
"extra": {
"downloadSettings": {
"address": "2001:db8::1",
"port": 443,
"network": "xhttp",
"tlsSettings": { "alpn": ["h3"], "serverName": "download.example.com" },
"xhttpSettings": {
"path": "/api/v2/",
"host": "download.example.com"
}
}
}
}
}
Заключение
XHTTP — это не просто очередной транспорт, а ответ на эволюцию систем цензуры. Отказавшись от вложенных TLS-рукопожатий и перейдя на уровень нативных HTTP-приложений, Xray сделал задачу детекции прокси экономически нецелесообразной для цензоров. По состоянию на конец 2024 года, уровень детекции правильно настроенного XHTTP составляет ~0%, так как его блокировка потребует нарушения работы значительной части легитимного интернета (CDN, API, SSE).