1. Байт-парсер SNI (RFC 6066)

Главным инженерным ядром CF-PROXY является функция extractSNI(buf) в файле bridge.mjs. Вместо создания полноценного TLS-терминатора (требующего ключей шифрования и подмены сертификатов), прокси анализирует входящий сырой буфер сокета на 443 порту в момент рукопожатия TLS.

Согласно спецификации RFC 6066 (TLS Server Name Indication), клиент при установке защищенного соединения отправляет имя запрашиваемого виртуального хоста в открытом виде (в заголовке ClientHello), чтобы целевой сервер мог выбрать нужный SSL-сертификат.

JavaScript // bridge.mjs (SNI Extractor)
function extractSNI(buf) {
  try {
    // Проверка сигнатуры TLS: Record Type 0x16 (Handshake) и Handshake Type 0x01 (ClientHello)
    if (buf.length < 43 || buf[0] !== 0x16 || buf[5] !== 0x01) return null;
    
    let pos = 43; // Смещение заголовочной части и случайных байтов клиента (Client Random)
    if (pos >= buf.length) return null;
    
    // Пропуск Session ID
    pos += 1 + buf[pos];
    if (pos + 2 > buf.length) return null;
    
    // Пропуск списка Cipher Suites
    pos += 2 + buf.readUInt16BE(pos);
    if (pos + 1 > buf.length) return null;
    
    // Пропуск методов компрессии (Compression Methods)
    pos += 1 + buf[pos];
    if (pos + 2 > buf.length) return null;
    
    // Чтение длины блока расширений TLS
    const extTotalLen = buf.readUInt16BE(pos);
    pos += 2;

    const end = Math.min(buf.length, pos + extTotalLen);
    
    // Итерация по блокам расширений в поисках Type 0x0000 (server_name)
    while (pos + 4 <= end) {
      const extType = buf.readUInt16BE(pos);
      const extLen = buf.readUInt16BE(pos + 2);
      pos += 4;
      if (extType === 0x0000 && pos + 5 <= end) {
        const nameLen = buf.readUInt16BE(pos + 3);
        if (pos + 5 + nameLen <= end) {
          return buf.toString("utf8", pos + 5, pos + 5 + nameLen);
        }
      }
      pos += extLen;
    }
  } catch (_) {}
  return null;
}

2. Анатомия пакета TLS ClientHello

Ниже представлена структура байтового смещения, по которой Node.js извлекает целевой хост без расшифровки:

Byte Layout // RFC 5246 & RFC 6066
+--------------------------------------------------------------------------+
| Offset (Hex/Dec) | Size    | Field Description                           |
+--------------------------------------------------------------------------+
| 0x00 (0)         | 1 Byte  | 0x16 - TLS Record ContentType (Handshake)   |
| 0x01 (1)         | 2 Bytes | TLS Protocol Version (0x0301 = TLS 1.0/1.2) |
| 0x03 (3)         | 2 Bytes | Length of Handshake Message Body            |
| 0x05 (5)         | 1 Byte  | 0x01 - HandshakeType (ClientHello)          |
| 0x06 (6)         | 3 Bytes | Handshake Length                            |
| 0x09 (9)         | 2 Bytes | Client Version (0x0303 = TLS 1.2 / 1.3)     |
| 0x0B (11)        | 32 Bytes| Random Data (Unix Timestamp + Random bytes) |
| 0x2B (43)        | 1+N Byte| Session ID Length + Session ID bytes        |
| Pos_A            | 2+N Byte| Cipher Suites Length + Cipher Suites array  |
| Pos_B            | 1+N Byte| Compression Methods Length + Methods bytes  |
| Pos_C            | 2 Bytes | Extensions Length                           |
| Extension:       |         |                                             |
|   +0             | 2 Bytes | Extension Type (0x0000 = server_name)       |
|   +2             | 2 Bytes | Extension Length                            |
|   +4             | 2 Bytes | Server Name List Length                     |
|   +6             | 1 Byte  | NameType (0x00 = Host_Name)                 |
|   +7             | 2 Bytes | Host Name Length                            |
|   +9             | N Bytes | Host Name String (ASCII: "chatgpt.com")     |
+--------------------------------------------------------------------------+

3. Безопасность Zero-CA

В традиционных прокси-серверах (Fiddler, Charles, mitmproxy) клиент соединяется с прокси, прокси подменяет SSL-сертификат сайта своим собственным и расшифровывает весь входящий и исходящий трафик.

Угрозы MITM-подхода

1. Установка стороннего Root CA в систему даёт возможность любому процессу с правами администратора расшифровывать банковские операции и пароли.
2. Большинство AI-инструментов (Google Antigravity, Cursor, расширения VS Code) используют встроенные хранилища сертификатов (Certificate Pinning) и наотрез отказываются работать через MITM-сертификаты.

Подход CF-PROXY: Мост вообще не генерирует ключей шифрования. После извлечения SNI-строки, сырые криптографические байты TLS ClientHello упаковываются в WebSocket и передаются удаленному серверу. Сквозной ключ симметричного шифрования (AES-GCM / ChaCha20-Poly1305) согласуется исключительно между вашим клиентом и сервером OpenAI / Google.

4. WebSocket Multiplexing

После того, как SNI распознан (например, chatgpt.com), узел инициирует исходящее соединение по протоколу WebSocket к шлюзу Cloudflare:

JavaScript
const ws = new WebSocket(`wss://${WORKER_HOST}/?host=${encodeURIComponent(sni)}&port=443`);
ws.binaryType = "arraybuffer";

Все последующие пакеты от клиента перенаправляются в метод ws.send(chunk), а входящие ответы от сервера передаются обратно клиенту через client.write(new Uint8Array(e.data)).

Благодаря флагу client.setNoDelay(true) отключается алгоритм Нейгла, обеспечивая минимальную задержку при передаче интерактивных потоков токенов (Stream Responses в ChatGPT и Antigravity).

5. Маршрутизация в Cloudflare Edge

Cloudflare Worker развернут на глобальной сети Anycast. При входящем WebSocket запросе он считывает query-параметры host и port и вызывает системный API connect():

TypeScript // Cloudflare Worker Edge Handler
import { connect } from 'cloudflare:sockets';

export default {
  async fetch(request) {
    const url = new URL(request.url);
    const targetHost = url.searchParams.get("host");
    const targetPort = parseInt(url.searchParams.get("port") || "443");

    const webSocketPair = new WebSocketPair();
    const [client, server] = Object.values(webSocketPair);
    server.accept();

    // Открытие сырого TCP-сокета к целевому серверу через сеть Cloudflare
    const tcpSocket = connect({ hostname: targetHost, port: targetPort });

    // Связывание потоков WebSocket <-> TCP
    pipeStreams(server, tcpSocket);

    return new Response(null, { status: 101, webSocket: client });
  }
};

6. Производительность и оверхед

// Задержка парсинга
< 0.05 мс
Синхронная проверка байтовых смещений буфера в оперативной памяти Node.js.
// Задержка DNS
0.2 мс
Локальный ответ 127.0.0.1 из памяти dnsmasq.
// Оверхед на заголовки
2–4 байта
Минимальный фрейминг стандартного WebSocket фрейма.
// Нагрузка на CPU
< 0.5% хоста
Отсутствие криптографических вычислений для расшифровки TLS.