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

У звичайних умовах це розглядається як фундаментальна властивість адресації. Однак під час виконання конфіденційних дій — передавання запитів до систем штучного інтелекту (LLM), надсилання звітів про збої в ОС, біометричної автентифікації чи географічних розрахунків — IP-адреса виступає стабільним ідентифікатором. Зіставляючи часові позначки та мережеві адреси, сервери цільових сервісів формують детальні профілі користувачів навіть за відсутності авторизації в системі.

Традиційні проксі-сервери лише зміщують вектор ризику: оператор проксі отримує повний доступ як до реальної IP-адреси пристрою, так і до відкритих назв серверів призначення. Для розв'язання цієї проблеми без потреби у централізованій довірі інженерна група IETF ухвалила специфікацію RFC 9458 (Oblivious HTTP, OHTTP), що спирається на стандарти 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        |
|          |                                                +------------+ |
|          +----------------------------------------------> |   Цільовий | |
|                 (Наскрізна криптографічна межа)           |    сервер  | |
|                 Сервер Target розшифровує через HPKE      |   (Target) | |
|                 Target бачить ЛИШЕ IP-АДРЕСУ ШЛЮЗУ RELAY  +------------+ |
|                                                                          |
+--------------------------------------------------------------------------+

Криптографічні засади: схема HPKE та формат Binary HTTP

Специфікація RFC 9458 базується на обов'язковому розмежуванні обов'язків між двома незалежними вузлами: шлюзом ретрансляції (Relay) та сервером призначення (Target). Шлюз Relay пересилає закриті пакети, не маючи можливості їх дешифрувати; сервер Target обробляє дані, але бачить виключно IP-адресу шлюзу ретрансляції.

Цей захист забезпечується двома базовими технологіями:

1. Бінарне кадрування Binary HTTP (RFC 9292)

Звичайні HTTP-запити складаються з текстових заголовків і довільного тіла пакета. В архітектурі OHTTP запит цілком перетворюється на стандартизований масив бінарних байтів за правилами RFC 9292 (BHTTP):

+--------------------+--------------------------------+--------------------+
| Керуючі поля кадру | Індексовані заголовки          | Корисне тіло       |
| (GET /api/v1/query)| (Host: target.example.com ...) | (Бінарні дані)     |
+--------------------+--------------------------------+--------------------+

Такий формат виключає розбіжності парсингу на транспортному рівні перед виконанням криптографічних дій.

2. Гібридне шифрування з відкритим ключем (HPKE, RFC 9180)

Для захисту бінарного запиту від аналізу на шлюзі Relay клієнт шифрує його за допомогою конфігурації відкритого ключа Target. Сервер Target надає публічну конфігурацію OHTTP:

  • 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, клієнт створює сесійний ключ шифрування $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})$$

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

Оскільки відповідний закритий ключ зберігається виключно на сервері Target, шлюз Relay технічно не може розкрити чи змінити корисні дані.

+--------------------------------------------------------------------------+
|                  РОЗПОДІЛ ДОСТУПУ ДО ДАНИХ У МЕРЕЖІ                      |
+--------------------------------------------------------------------------+
| Сторона      | Бачить реальну IP? | Читає тіло запиту? | Читає відповідь?|
+--------------+--------------------+--------------------+-----------------+
| Провайдер    | Так                | НІ (Захист TLS)    | НІ              |
| Шлюз Relay   | Так (або IP VPN)   | НІ (Шифр HPKE)     | НІ              |
| Сервер Target| НІ (Бачить Relay)  | ТАК (Розшифровано) | ТАК             |
+--------------------------------------------------------------------------+

Послідовність виконання сесії Oblivious HTTP

Процедура обміну даними за протоколом OHTTP складається з п'яти дій:

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

Взаємодія з протоколом RiceGuard та мережевим рівнем L3

Незважаючи на те, що Oblivious HTTP працює на прикладному рівні (L7), його поєднання з тунелями третього рівня (L3) протоколу RiceGuard сервісу VPNRice формує комплексний багаторівневий захист:

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

Програмна реалізація клієнта OHTTP мовою Go

Нижче наведено робочий приклад клієнтського модуля мовою 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 пакує бінарне повідомлення у контейнер 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) збільшує час кругового обігу пакетів (RTT) на 20–45 мс. Протокол якнайкраще підходить для API-викликів, телеметрії та AI-запитів, але не для завантаження важких веб-сторінок.
  • Особливості кешування: Мережі CDN кешують контент на основі аналізу URL-шляхів. Через непрозорість тіла OHTTP проміжне кешування вимагає спеціальних тегів.
  • Організаційна незалежність: Модель безпеки втрачає чинність, якщо Relay і Target контролюються однією компанією. Обов'язковою вимогою є експлуатація цих компонентів незалежними операторами.

Специфікація RFC 9458 Oblivious HTTP підтверджує, що сучасний інтернет здатен забезпечувати надійну конфіденційність без потреби жертвувати сумісністю із веб-стандартами.