Протокол транспортной безопасности TLS 1.3 гарантирует конфиденциальность передаваемых пакетов при их прохождении через узлы связи интернет-провайдеров. Однако под защитным слоем шифрования сохраняется фундаментальная уязвимость на уровне метаданных: целевой сервер всегда видит исходящий IP-адрес клиента.

В базовых сценариях взаимодействия это считается естественной частью сетевой маршрутизации. Но при выполнении конфиденциальных задач — отправке запросов к генеративным языковым моделям (LLM), передаче телеметрии сбоев, биометрической верификации или обращении к геолокационным службам — IP-адрес превращается в устойчивый цифровой идентификатор. Сопоставляя временные метки сессий и сетевые адреса, серверные системы формируют детальные профили активности пользователей даже при отсутствии авторизации в учетных записях.

Традиционные прокси-серверы лишь переносят точку наблюдения: оператор прокси видит одновременно реальный адрес клиента и открытый адрес назначения. Для разрыва этой связи IETF ратифицировала спецификацию RFC 9458 (Oblivious HTTP), дополненную стандартами RFC 9180 (Hybrid Public Key Encryption, HPKE) и RFC 9292 (Binary HTTP).

+--------------------------------------------------------------------------+
|                    АРХИТЕКТУРА RFC 9458 OBLIVIOUS HTTP                   |
+--------------------------------------------------------------------------+
|                                                                          |
|    +------------+        1. Зашифрованный запрос (HPKE)   +------------+ |
|    |            | --------------------------------------> |   Шлюз     | |
|    |   Клиент   | <-------------------------------------- | ретрансля- | |
|    | (Браузер / |        4. Зашифрованный ответ (HPKE)    | ции (Relay)| |
|    |  AI Agent) |                                         +------------+ |
|    +------------+                                               |        |
|          |                                            2. Пересыл|3. Ответ|
|          | (IP-адрес клиента виден ТОЛЬКО Relay)      шифрован- |шифрован|
|          | (Данные зашифрованы публичным ключом Target)ного тела|ного тела
|          |                                                      v        |
|          |                                                +------------+ |
|          +----------------------------------------------> |  Целевой   | |
|                 (Сквозная криптографическая граница)      |   сервер   | |
|                 Сервер расшифровывает тело через HPKE     |  (Target)  | |
|                 Сервер видит ТОЛЬКО IP-АДРЕС ШЛЮЗА RELAY  +------------+ |
|                                                                          |
+--------------------------------------------------------------------------+

Криптографические основы: схема HPKE и бинарный формат Binary HTTP

Спецификация RFC 9458 опирается на принцип недопущения сговора между двумя независимыми инфраструктурными узлами: шлюзом ретрансляции (Relay) и целевым сервером (Target). Шлюз Relay пересылает зашифрованные бинарные контейнеры, не имея ключа для их вскрытия; целевой сервер Target обрабатывает полезную нагрузку, но видит исключительно сетевой адрес шлюза ретрансляции.

Надежность протокола базируется на двух ключевых компонентах:

1. Кадрирование Binary HTTP (RFC 9292)

Стандартные запросы HTTP состоят из текстовых строк заголовков и произвольного тела пакета. В протоколе OHTTP запрос целиком преобразуется в детерминированный бинарный массив байтов согласно спецификации RFC 9292 (BHTTP):

+--------------------+--------------------------------+--------------------+
| Управляющие поля   | Индексированные заголовки      | Тело полезной      |
| (GET /api/v1/query)| (Host: target.example.com ...) | нагрузки (бинарное)|
+--------------------+--------------------------------+--------------------+

Бинарное представление исключает разночтения на уровне транспортных протоколов до выполнения криптографических процедур.

2. Гибридное шифрование с открытым ключом (HPKE, RFC 9180)

Для защиты бинарного HTTP-запроса от перехвата на шлюзе Relay клиент шифрует его с использованием конфигурации публичного ключа целевого сервера Target. Конфигурация включает:

  • key_id: 1-байтовый идентификатор для поддержки ротации ключей;
  • kem_id: идентификатор механизма инкапсуляции ключей (0x0020 для DHKEM над Curve25519);
  • kdf_id: идентификатор функции деривации ключей (0x0001 для HKDF-SHA256);
  • aead_id: идентификатор шифра с аутентификацией данных (0x0001 для AES-128-GCM или 0x0003 для ChaCha20-Poly1305);
  • public_key: байтовая последовательность открытого ключа Target.

В базовом режиме HPKE клиент генерирует эфемерную пару ключей $(sk_E, pk_E)$ и вычисляет общий секрет с открытым ключом целевого сервера $pk_T$:

$$ ext{SharedSecret} = ext{DH}(sk_E, pk_T)$$

С помощью алгоритма HKDF клиент формирует ключ AEAD-шифрования $K_{ ext{req}}$ и вектор инициализации $N_{ ext{req}}$:

$$(K_{ ext{req}}, N_{ ext{req}}) = ext{KeyDerivation}( ext{SharedSecret}, ext{info})$$

Бинарный запрос BHTTP запечатывается в шифротекст $C_{ ext{req}}$:

$$C_{ ext{req}} = ext{Seal}(K_{ ext{req}}, N_{ ext{req}}, ext{aad}, ext{BinaryHTTPRequest})$$

Сообщение, направляемое на шлюз Relay, имеет медиа-тип message/ohttp-req и структуру: [KeyID (1 байт)] || [KEM (2 байта)] || [KDF (2 байта)] || [AEAD (2 байта)] || [enc (32 байта)] || [Шифротекст]

Поскольку секретным ключом от $pk_T$ обладает только Target, шлюз Relay технически не способен прочитать или модифицировать полезную нагрузку.

+--------------------------------------------------------------------------+
|                  ВИДИМОСТЬ ДАННЫХ ДЛЯ УЧАСТНИКОВ ОБМЕНА                  |
+--------------------------------------------------------------------------+
| Субъект     | Видит реальный IP клиента? | Видит тело запроса? | Видит ответ?|
+-------------+----------------------------+---------------------+-------------+
| Провайдер   | Да                         | НЕТ (TLS-туннель)   | НЕТ         |
| Шлюз Relay  | Да (или IP VPN)            | НЕТ (Шифротекст)    | НЕТ         |
| Сервер Target| НЕТ (Видит адрес Relay)   | ДА (Расшифровано)   | ДА          |
+--------------------------------------------------------------------------+

Жизненный цикл сессии Oblivious HTTP

Взаимодействие в рамках стандарта OHTTP состоит из пяти шагов:

  1. Получение конфигурации ключей: Клиент считывает конфигурацию открытых ключей Target через записи DNS HTTPS или доверенные службы каталогов.
  2. Формирование зашифрованного контейнера: Клиент сериализует запрос в Binary HTTP, создает эфемерную пару ключей HPKE, запечатывает пакет и формирует тело message/ohttp-req.
  3. Ретрансляция через Relay: Клиент отправляет пакет на Relay методом HTTPS POST /relay. Шлюз Relay завершает входящую сессию TLS, удаляет сетевые заголовки клиента и направляет полезную нагрузку на Target по новому соединению.
  4. Обработка на сервере Target: Целевой сервер сопоставляет key_id, выполняет декапсуляцию enc, восстанавливает общий секрет, расшифровывает полезную нагрузку и извлекает Binary HTTP. Выполняется бизнес-логика (например, инференс AI), а результат упаковывается в Binary HTTP.
  5. Инкапсуляция ответа: Target генерирует сессионный ключ $K_{ ext{res}}$ из контекста HPKE, запечатывает ответ в тело message/ohttp-res и отправляет его обратно на Relay. Шлюз Relay пересылает контейнер клиенту для локальной расшифровки.

Интеграция с протоколом RiceGuard и сетевым уровнем L3

Хотя протокол Oblivious HTTP функционирует на прикладном уровне (L7), его объединение с туннелями третьего уровня (L3) обеспечивает эшелонированную защиту:

  1. Разрыв профилирования на стороне Relay: При передаче трафика через VPNRice и протокол RiceGuard сессия с шлюзом Relay устанавливается с общего пула IP-адресов VPN, а не с домашнего подключения. Даже при компрометации шлюза Relay сопоставить активность с конкретным абонентом невозможно.
  2. Защита от утечек DNS: Запросы к системе доменных имен при поиске шлюзов Relay и Target изолированы внутри сетевого пространства RiceGuard.
  3. Конфиденциальность локальных агентов AI: Автономные агенты искусственного интеллекта при вызове внешних API могут направлять сетевые запросы через OHTTP поверх туннелей RiceGuard, исключая сохранение клиентских IP-адресов поставщиками нейросетей.

Реализация клиента OHTTP на языке Go

Ниже приведен пример реализации клиентской части, выполняющей инкапсуляцию запроса Binary HTTP по стандарту RFC 9458:

package main

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"io"
	"net/http"

	"github.com/cloudflare/circl/hpke"
)

type OHTTPEncapsulator struct {
	suite    hpke.Suite
	targetPK hpke.PublicKey
	keyID    uint8
}

func NewOHTTPEncapsulator(keyID uint8, kemID hpke.KEM, kdfID hpke.KDF, aeadID hpke.AEAD, rawPubKey []byte) (*OHTTPEncapsulator, error) {
	suite := hpke.NewSuite(kemID, kdfID, aeadID)
	pk, err := suite.KEM().Scheme().UnmarshalBinaryPublicKey(rawPubKey)
	if err != nil {
		return nil, fmt.Errorf("ошибка парсинга открытого ключа: %w", err)
	}
	return &OHTTPEncapsulator{
		suite:    suite,
		targetPK: pk,
		keyID:    keyID,
	}, nil
}

// EncapsulateRequest упаковывает Binary HTTP в контейнер message/ohttp-req
func (oe *OHTTPEncapsulator) EncapsulateRequest(bhttpReq []byte) ([]byte, hpke.Context, error) {
	sender, err := oe.suite.NewSender(oe.targetPK, []byte("message/bhttp request"))
	if err != nil {
		return nil, nil, fmt.Errorf("ошибка инициализации HPKE sender: %w", err)
	}

	enc, ctx, err := sender.Setup(rand.Reader)
	if err != nil {
		return nil, nil, fmt.Errorf("ошибка настройки HPKE: %w", err)
	}

	ciphertext, err := ctx.Seal(nil, bhttpReq)
	if err != nil {
		return nil, nil, fmt.Errorf("ошибка шифрования данных: %w", err)
	}

	var buf bytes.Buffer
	buf.WriteByte(oe.keyID)
	buf.Write([]byte{0x00, 0x20}) // DHKEM(X25519, HKDF-SHA256)
	buf.Write([]byte{0x00, 0x01}) // HKDF-SHA256
	buf.Write([]byte{0x00, 0x01}) // AES-128-GCM
	buf.Write(enc)
	buf.Write(ciphertext)

	return buf.Bytes(), ctx, nil
}

func SendToRelay(relayURL string, targetURL string, ohttpPayload []byte) ([]byte, error) {
	req, err := http.NewRequest("POST", relayURL, bytes.NewReader(ohttpPayload))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "message/ohttp-req")
	req.Header.Set("Target", targetURL)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("ошибка отправки на релей: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("ошибка обработки шлюзом: HTTP %d", resp.StatusCode)
	}

	return io.ReadAll(resp.Body)
}

Инженерные компромиссы и специфика внедрения

При проектировании систем с использованием RFC 9458 необходимо учитывать следующие факторы:

  • Дополнительная сетевая задержка: Прохождение пакетов через два физических серверных узла (Клиент $ o$ Relay $ o$ Target) добавляет около 20–45 мс к общему времени отклика (RTT). Протокол оптимизирован для дискретных запросов к API и телеметрии, а не для интерактивного веб-серфинга.
  • Особенности кэширования: Пограничные узлы CDN кэшируют ответы на основе анализа URL. Поскольку тела запросов в OHTTP непрозрачны, промежуточное кэширование требует специальных меток.
  • Независимость субъектов: Надежность схемы аннулируется при нахождении Relay и Target под единым административным контролем. Обязательным условием является распределение ролей между независимыми операторами инфраструктуры.

Стандарт RFC 9458 подтверждает, что защита сетевой конфиденциальности достигается четким математическим разделением идентификаторов и данных.