Протокол WebSocket позволяет устанавливать постоянное двустороннее соединение между клиентом и сервером, что значительно снижает задержки и уменьшает объем передаваемых данных по сравнению с традиционными HTTP-запросами. WebSocket используется в динамически обновляющихся приложениях, работающих в режиме реального времени, – мессенджерах, торговых площадках и других коммерческих сервисах с быстро меняющимися данными. Благодаря корректным правилам фильтрации протокола WebSocket такой трафик может быть распознан и, при необходимости, заблокирован.
Меня зовут Александр Лемаев, я ведущий аналитик в команде Solar webProxy. Solar webProxy — это шлюз веб-безопасности (SWG), который разграничивает доступ к веб-ресурсам, защищает от зараженных и фишинговых сайтов и блокирует утечки через веб-канал. В статье я расскажу, в чем преимущества WebSocket, и как наш прокси-сервер Solar webProxy помогает фильтровать этот протокол.
Что такое протокол WebSocket и как он упрощает клиенто-серверное взаимодействие
Протокол передачи данных WebSocket позволяет устанавливать двустороннюю полнодуплексную связь с низкой задержкой в режиме реального времени между клиентом и сервером. В отличие от традиционного протокола НТТР, WebSocket может работать в асинхронном режиме. То есть, клиент и сервер могут отправлять друг другу данные без непрерывного или предварительного опроса. Также WebSocket стабилен при помехах — если они возникли, то соединение не придется переустанавливать. Как только связь восстановится, данные будут переданы.
WebSocket работает поверх транспортного протокола TCP. Для установления связи по WebSocket используется HTTP, а сам процесс называется WebSocket Handshake (рукопожатие). Для подключения используются схемы ws:// или wss://, которые аналогичны схемам http:// и https://.
Запрос для соединения с сервером по WebSocket со стороны клиента выглядит так:
Ответ от сервера на запрос клиента выглядит так:
Клиент ожидает увидеть в ответе: HTTP 101 Switching Protocols. Это значит, что сервер переключается на протокол, запрошенный клиентом — то есть, WebSocket.
Есть несколько типичных сценариев, в которых целесообразно использовать протокол WebSocket, например:
Обновление в режиме реального времени. Благодаря этому WebSocket идеально подходит для ресурсов, где важно мгновенное обновление информации — например, трейдерских или банковских приложений. Пользователи смогут следить за информацией онлайн, не перезагружая постоянно страницу приложения.
Высокочастотная передача данных. WebSocket может использоваться для мессенджеров, стриминговых сервисов, видеоконференций, чатов в социальных сетях — везде, где важна скорость передачи данных.
Синхронизация. WebSocket позволяет нескольким пользователям одновременно редактировать онлайн-документ — внесенные изменения отображаются мгновенно. Также протокол помогает отслеживать время каждой корректировки и смотреть за работой коллег в реальном времени.
HTTP vs WebSocket
HTTP и WebSocket схожи по функциям, но служат различным целям и отличаются по принципам работы.
Характеристика |
HTTP |
WebSocket |
Модель взаимодействия |
Однонаправленная: |
Полнодуплексная (двунаправленная). Поддерживает работу в асинхронном режиме — клиент и сервер могут отправлять данные друг другу в любое время. |
Безопасность и шифрование |
Адрес незашифрованной версии начинается с http://, зашифрованной — с https:// |
Адрес незашифрованной версии начинается с ws://, зашифрованной — с wss:// + использует маскировку тела данных |
Кэширование |
Только статические данные для ускорения отправки ответа |
Не поддерживает кэширование — только обновление в режиме реального времени |
Транспорт |
Работает с TCP, но начиная с HTTP3, использует протокол UDP для ускорения (QUIC) |
Поддерживает только TCP, так как протоколу важна стабильность |
Задержка |
Для каждого запроса требуется новое рукопожатие и согласование заголовков — то есть, задержка выше, чем у WebSocket |
Нет необходимости в частых рукопожатиях, согласовании заголовков и длительных опросах. Сообщения отправляются с помощью фреймов без заголовков, так как соединение не прерывается — то есть, задержка ниже, чем у HTTP |
Продолжительность соединения |
Требуется новое подключение для каждого запроса. Соединение длится до получения запроса-ответа |
Поддерживает постоянное соединение с мгновенной передачей данных без постоянных циклов запроса-ответа |
Поддержка двоичных данных |
По умолчанию поддерживает только текстовые данные. Для обработки двоичных данных нужна поддержка дополнительных протоколов или кодирования (например, Base64) |
Поддерживает двоичные данные изначально — можно быстро передавать мультимедийный контент |
Проблемы безопасности протокола WebSocket
Несмотря на преимущества перед HTTP, у разработчиков неоднозначное отношение к WebSocket. Передача данных без закрытия соединения после запроса или ответа делает информацию уязвимой для хакеров. Например, ранние версии протокола подвергались атаке «Отравление кэша», которая была нацелена на кэширующие прокси-серверы.
Как происходила атака «Отравление кэша» Злоумышленник приглашал клиента посетить мошенническую веб-страницу, которая устанавливала соединение по WebSocket с сайтом хакера. Предполагалось, что жертва сидит за прокси-сервером. Хакерская страница формировала по WebSocket запрос для отправки сообщения на сервер мошенника. Прокси-сервер пропускал его, ожидая следующего запроса для соединения по HTTP. Но на самом деле связь не прерывалась, а продолжала работать через WebSocket. Оба конца открытого соединения контролировались хакером, который мог свободно передавать через него вредоносные данные. Так как прокси-сервер закэшировал эти данные, каждый его пользователь получал код хакера вместо реального кода jQuery. |
Риск атаки «Отравление кэша» сначала был теоретическим, но анализ уязвимости WebSocket показал, что она действительно может произойти. Тогда разработчики протокола ввели маскировку данных, которая успешно используется для защиты обеих сторон соединения. Данные шифруются с помощью сгенерированного случайным образом 4-байтового значения (ключа) с использованием заранее определенного алгоритма.
Какие еще атаки могут повлиять на WebSocket:
Межсайтовый перехват (Cross-site hijacking) — один из сценариев межсайтовой подделки запросов (CSRF). Атака допустима, когда рукопожатие для WebSocket-соединения происходит через куки HTTP без использования CSRF-токенов или аналогичных мер безопасности.
Инъекция кода (injection attacks) — внедрение кода злоумышленника в уязвимое приложение. Это одна из самых распространенных атак на протокол НТТР, но она также применима и к WebSocket. Самые популярные — SQL-инъекции, когда хакеры манипулируют запросами при работе с базами данных, а также межсайтовый скриптинг (XSS), где вредоносные скрипты внедряются в веб-страницы.
Отказ в обслуживании (DOS) — атака веб-сервера, поддерживающего WebSocket. Злоумышленник направляет большое количество запросов на сервер, приводя его к аварийному завершению или перезагрузке. Распространенные атаки такого класса — WebSocket Flood, WebSocket Ping Pong, WebSocket Slowloris. Подробнее можно прочитать здесь.
WebSocket Smuggling — эта атака позволяет хакеру обойти ограничения обратных прокси-серверов, заставив их поверить, что соединение по WebSocket было установлено (даже если это не так). Подробнее читайте здесь.
Один из способов предотвратить эти атаки — фильтрация трафика по WebSocket c помощью прокси-сервера Solar webProxy.
Как фильтровать WebSocket с помощью Solar webProxy
В версии Solar webProxy 4.1 появилась возможность фильтровать WebSocket. Для этого нужно зайти в раздел «Политика», затем — в подраздел «Контентная фильтрация», затем — в слой «Фильтрация запросов» при создании правил и исключений, и здесь в расширенных настройках можно выбрать WebSocket и Websocket Secure.
Допускается выбрать протоколы WebSocket и/или WebSocket Secure и в слое Фильтрации ответов, но тогда Solar webProxy будет фильтровать ответы, ожидая “101 Switching Protocols” от сервера приложения.
В предыдущих версиях продукта нельзя было выбрать WebSocket или WebSocket Secure в слоях «Фильтрация запросов» и «Фильтрация ответов». Из доступных протоколов были только HTTP, HTTPS и/или FTP (over HTTP).
Пример блокировки протокола WebSocket для платформы обмена криптовалютами BitMEX:
Как оповестить пользователя о блокировке WebSocket
Если администратор хочет уведомить пользователя о намеренной блокировке WebSocket, он может создать правила в слоях фильтрации запросов или ответов:
Правило на блокировку протокола WebSocket/WebSocket Secure;
Правило на оповещения пользователя.
Если администратор не планирует блокировать WebSocket, но хочет предупредить пользователя о неисправностях в работе протокола со стороны веб-ресурса, то можно создать правило на вывод JavaScript-плашки для пользователя — без блокирующих правил для WebSocket в слое.
Пример уведомления на сайте Coinmarketcap, где не обновляются котировки цен:
При получении плашки некоторые функции ресурса будут недоступны, например:
обновление информации в реальном времени;
обмен сообщениями в чате;
подключение к стримам или конференциям.
Плашка будет появляться при инициализации протокола WebSocket, перехваченного в главном потоке. Поэтому если закрыть плашку, она появится снова, так как некоторые элементы страницы не будут подгружаться.
Почему трудно анализировать фреймы WebSocket в Solar webProxy
Все взаимодействия между клиентами и серверами в рамках WebSocket-соединений происходят при помощи фреймов — небольших заголовков с множеством битов и «полезной нагрузкой». Полезная нагрузка — это все данные приложения, аналогичные телу сообщения HTTP.
Фрейм выглядит так:
Фреймы делятся на два типа:
с данными (non-control frames);
для управления (control frames) — они нужны для проверки и закрытия соединения.
Сообщения между клиентом и сервером подвергаются фрагментации — то есть, одно сообщение может быть разбито на несколько фреймов с данными. В рамках WebSocket-соединения управляющий фрейм может быть в середине фрагментированного сообщения, но не допускается отправка фрейма с посторонними данными в одном из фрагментов. Фрагментация управляющих фреймов невозможна, а полезная нагрузка должна быть меньше 125 байт. Полное описание работы протокола и полей содержится в RFC 6455.
В Solar webProxy 4.1, в слоях «Фильтрация запросов» и «Фильтрация ответов», можно создавать правила и исключения с дополнительными условиями по заголовкам, типам и размеру файлов, ключевым словам. Но у каждого условия есть свои сложности:
Формат описания заголовков в Solar webProxy больше характерен для заголовков HTTP. Не совсем ясно, как обозначать этот «заголовок» для фреймов, так как он представляет собой набор полей со значениями;
Передаваемые по WebSocket файлы с большой вероятностью будут фрагментированы. Понять по «кусочкам» тип и размер файла сложно. Но можно обратить внимание на биты "FIN" и "Опкод".
Атрибут «Ключевые слова» позволяет выбрать слова и их вес и обозначить порог срабатывания. Но при при фрагментации данных невозможно будет корректно отфильтровать сообщения, так как ключевые слова тоже будут фрагментированы.
Дополнительная сложность — маскировка данных, которая защищает от хакерских атак, так как она шифрует содержимое, и ключевые слова сложно распознать.
Дальнейшие разработки по фильтрации WebSocket
В Solar webProxy фильтрацией данных (HTTP, HTTPS, HTTP over FTP) занимается сервис skvt-wizor. После установки WebSocket-соединения между клиентом и сервером мы планируем скачивать все фреймы для получения полных сообщений. Начало и конец сообщений будем определять с помощью битов "FIN" и "Опкод". Если сообщение будет состоять из одного фрейма, то проверим его содержание. Если сообщение будет фрагментировано, то запишем каждый фрейм в виде отдельного файла для дальнейшей отправки на проверку.
Результат проверки будет зависеть от настроенной политики в контентной фильтрации. Полноценная проверка будет включать обнаружение MIME-типа, извлечение метаданных и текста, сканирование объекта в ICAP-сервере (антивирус, песочница или DLP-система) и фильтрацию по условиям в правилах/исключениях — например, по ключевым словам или размеру файлов.
В Solar webProxy у каждого фрейма будет своя имплементация внутриобъектовый сущности, которая будет определять:
необходимость кэширования объекта на диск либо хранения в оперативной памяти;
необходимость в распаковке — если применено сжатие;
необходимость в передаче данных фильтрующим узлам — если требуется проверка объекта.
Маску будем извлекать из метаданных фрейма, после этого демаскируем полезную нагрузку, записав полученные данных в объектную сущность Solar webProxy.
Заключение
Фильтрация протокола WebSocket по фреймам пока находится на этапе исследования. Скорее всего, мы внесем изменения в будущую реализацию этой функции, но пока решили поделиться ей, так как не обнаружили других практических решений по фильтрации фреймов WebSocket.
Благодарю за внимание к статье! Буду рад комментариям и обратной связи.