Туннель, передаваемый внутри защищённого WebSocket, имеет ровно столько шифрования, сколько в него положил клиент, и не больше. Схема wss:// говорит, что TLS защищает одно соединение на одном переходе. Она ничего не говорит о том, защищены ли байты внутри сообщений WebSocket отдельно, и именно это различие решает, кто сможет читать ваш трафик, когда что-то посередине завершает TLS.

Два слоя, которые выглядят как один

Защищённый WebSocket — это TLS, поверх которого выполняется рукопожатие WebSocket, как описывает RFC 6455 в разделах о соединении и безопасности. Уровень TLS аутентифицирует сервер и шифрует поток, затем уровень WebSocket кадрирует то, что приложение решит отправить.

Это два независимых решения, и схема, рисующая один замок на всём пути, скрывает второе. Спросите, кто завершает соединение TLS. Затем спросите, что клиент кладёт внутрь каждого сообщения. Всё остальное о развёртывании следует из этих двух ответов.

Схема A: транспорт — единственное шифрование

В более простой схеме клиент берёт необработанные IP-пакеты и заворачивает их в сообщения WebSocket. Внутри ничего не шифруется, поэтому компонент, держащий сеанс TLS, читает нагрузки в открытом виде. Когда этот компонент — ваш собственный сервер туннеля, это и есть предполагаемая конечная точка, и схема согласована, потому что сервер всё равно видел бы трафик.

Это становится другим раскладом, когда на пути стоит прокси проверки и завершает TLS сертификатом, которому клиент доверяет. Теперь этот прокси держит тот же открытый текст, что держал бы сервер, и никакое количество кадрирования WebSocket этого не меняет. Хранилище доверия клиента решило этот исход задолго до открытия туннеля, потому что выбрало принять этот сертификат для этого имени. Прокси также видит запрошенный хост и размер каждого сообщения, чего достаточно, чтобы снять отпечаток множества действий, ничего больше не расшифровывая.

Схема B: нагрузка несёт собственные ключи

Во второй схеме клиент шифрует и аутентифицирует свои пакеты протоколом VPN, у которого есть собственная идентичность узла и обмен ключами, а затем несёт результат внутри сообщений WebSocket. Завершение внешнего TLS здесь открывает шифротекст, а не трафик. Прокси видит размеры и время сообщений, и ничего больше в нагрузке. Это гораздо меньшая экспозиция, и всё же это метаданные, которые стоит назвать в обзоре, а не отмахнуться от них.

Собственные конечные точки внутреннего протокола по-прежнему видят открытый текст, в этом смысл расклада и одновременно его предел. Если компонент на конечной точке уполномочен читать данные до шифрования или после расшифровки, дополнительный уровень его не ограничивает. Ключи и программное обеспечение, которое ими управляет, остаются границей, которая имеет значение.

Метка VPN, знакомый значок клиента или маркетинговая страница не являются доказательством существования этой схемы. Чтение формата нагрузки — является.

Проследите, где на самом деле появляется открытый текст

Схема URL даёт вам один факт, остальное получите из интерфейсов. Выполните проверки ниже по порядку, потому что каждая сужает следующую:

ss -tnp | grep -E ':(443|8443)\b'
ip -br link show type tun
openssl s_client -connect vpn.example.net:443 -servername vpn.example.net -brief
tcpdump -ni tun0 -c 5 -X

Начните с внешнего соединения. Один долгоживущий сеанс TCP к порту TLS — так выглядит носитель WebSocket, а второй сеанс к несвязанному хосту заслуживает объяснения, прежде чем смотреть глубже. Затем найдите интерфейс туннеля на клиенте, потому что устройство tun или tap говорит о том, что клиент обрабатывает IP-пакеты, а не проксирует один поток приложения.

Захват на интерфейсе туннеля обычно решает вопрос нагрузки. Читаемые заголовки там означают, что внутренние пакеты никогда не шифровались отдельно, а нагрузки с высокой энтропией означают, что шифровались. Повторите захват на внешнем интерфейсе и сравните оба: если оба выглядят случайными, внутренний уровень делает работу, которую внешний не может отменить.

Пересмотрите доверие, которое вы действительно выдали

Установленный корпоративный корневой сертификат — это факт о хранилище доверия клиента, а не утверждение о том, что какой-либо прокси регистрирует или проверяет. Установите развёрнутую политику и реальный путь соединения вместо того, чтобы выводить поведение из наличия CA.

На управляемом оборудовании спросите администратора, какой компонент завершает TLS, проверяет ли клиент закреплённый сертификат или публичный, и каков формат нагрузки. Эти три ответа дают вывод, который вы сможете защитить позже.

Запишите результат как границу, а не как приговор. "TLS завершается на прокси проверки, а внутренняя нагрузка отдельно зашифрована ключами, которые есть только у конечных точек" — это проверяемое утверждение, которое переживёт смену поставщика. "WebSockets безопасны" — нет.

Похожие статьи