Невозможно отучить людей изучать самые ненужные предметы.
Введение в CSS
Преимущества стилей
Добавления стилей
Типы носителей
Базовый синтаксис
Значения стилевых свойств
Селекторы тегов
Классы
CSS3
Надо знать обо всем понемножку, но все о немногом.
Идентификаторы
Контекстные селекторы
Соседние селекторы
Дочерние селекторы
Селекторы атрибутов
Универсальный селектор
Псевдоклассы
Псевдоэлементы
Кто умеет, тот делает. Кто не умеет, тот учит. Кто не умеет учить - становится деканом. (Т. Мартин)
Группирование
Наследование
Каскадирование
Валидация
Идентификаторы и классы
Написание эффективного кода
Вёрстка
Изображения
Текст
Цвет
Линии и рамки
Углы
Списки
Ссылки
Дизайны сайтов
Формы
Таблицы
CSS3
HTML5
Блог для вебмастеров
Новости мира Интернет
Сайтостроение
Ремонт и советы
Все новости
Справочник от А до Я
HTML, CSS, JavaScript
Афоризмы о учёбе
Статьи об афоризмах
Все Афоризмы
Помогли мы вам |
В этой статье мы разберем протокол WebSocket, подробно остановимся на уязвимости CSWSH — насколько она распространена в открытом интернете. Для тех, кто дочитает до конца, я приготовил бонус в виде утилиты cswsh-scanner, с помощью которой ты можешь проверить свои приложения, работающие с WebSocket, либо попытать удачи на баг‑баунти.
Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи.
Итак, что такое WebSocket? Википедия дает следующее определение: «WebSocket — протокол связи поверх TCP-соединения, предназначенный для обмена сообщениями между браузером и веб‑сервером в режиме реального времени». В отличие от синхронного протокола HTTP, построенного по модели «запрос — ответ», WebSocket полностью асинхронный и симметричный. Он применяется для организации чатов, онлайн‑табло и создает постоянное соединение между клиентом и сервером, которое обе стороны могут использовать для отправки данных.
Протокол WebSocket определен в RFC 6455. Для протокола зарезервированы две URI-схемы:
ws://host[:port]path[?query]
;wss://host[:port]path[?query]
.WebSocket достаточно распространен в современной веб‑разработке, есть поддержка во всех популярных языках программирования и браузерах. Его используют в онлайн‑чатах, досках объявлений, веб‑консолях, приложениях трейдеров. С помощью поисковика shodan.io можно с легкостью найти приложения на WebSocket, доступные из интернета. Достаточно сформировать простой запрос. Я не поленился и сделал:
Search for Sec-WebSocket-Version HTTP/1.1 400 Bad Request returned 55,461 results on 10-05-2020
В результате нашлось 55 тысяч адресов с обширной географией.
Разберем теперь, как работает WebSocket. Взаимодействие между клиентом и сервером начинается с рукопожатия. Для рукопожатия клиент и сервер используют протокол HTTP, но с некоторыми отличиями в формате передаваемых сообщений. Не соблюдаются все требования к HTTP-сообщениям. Например, отсутствует заголовок Content-Length
.
Для начала клиент инициирует соединение и отправляет запрос серверу:
GET /echo HTTP/1.1
Host: localhost:8081
Sec-WebSocket-Version: 13
Origin: http://localhost:8081
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Connection: keep-alive, Upgrade
Upgrade: websocket
Заголовки Sec-WebSocket-Version
, Sec-WebSocket-Key
, Connection:
обязательны, иначе сервер возвращает статус HTTP/
. Сервер отвечает на запрос клиента следующим образом:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Заголовок Sec-WebSocket-Key
формируется клиентом как случайное 16-байтовое значение, закодированное в Base64. Вариант формирования заголовка на Go:
func generateChallengeKey() (string, error) {
p := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, p); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(p), nil
}
Заголовок Sec-WebSocket-Accept
в ответе формируется по следующему алгоритму. Берется строковое значение из заголовка Sec-WebSocket-Key
и объединяется с GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
. Далее вычисляется хеш SHA-1 от полученной в первом пункте строки. Хеш кодируется в Base64. Вариант формирования заголовка на Go:
const GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
func computeAcceptKey(challengeKey string) string {
h := sha1.New()
h.Write([]byte(challengeKey + GUID))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
Заголовки Sec-WebSocket-Key
и Sec-WebSocket-Accept
не используются для авторизации и поддержки сессий, они служат для того, чтобы стороны убедились, что запрос и ответ относятся к протоколу WebSocket. Это помогает гарантировать, что сервер не принимает от клиентов запросы, не относящиеся к WebSocket.
Также RFC 6455 предполагает, что Sec-WebSocket-Key
должен быть выбран случайным образом для каждого соединения. Это означает, что любой кешированный результат от прокси‑сервера будет содержать невалидный Sec-WebSocket-Accept
и, следовательно, рукопожатие провалится вместо непреднамеренного чтения кешированных данных. Для успешного завершения рукопожатия клиент проверяет значение Sec-WebSocket-Accept
и ожидает статус‑код 101
. После того как рукопожатие выполнено, первоначальное соединение HTTP заменяется соединением по WebSocket, которое использует то же соединение TCP/IP. На этом этапе любая из сторон может начать отправку данных.
Для мониторинга трафика WebSocket удобно использовать «Инструменты разработчика», доступные, к примеру, в Chrome.
Как в WebSocket передаются сообщения? Данные по протоколу WebSocket передаются как последовательность фреймов. Фрейм имеет заголовок, в котором содержится следующая информация:
Формат фрейма представлен на рисунке.
Все сообщения, посылаемые клиентом, должны маскироваться. Пример отправки тестового сообщения «Hello world!» клиентом (данные из tcpdump):
Fin: True
Reserved: 0x0
Opcode: Text (1)
Mask: True
Payload length: 12
Masking-Key: a2929b01
Payload: eaf7f76dcdb2ec6ed0feff20
Маскировка производится обычным XOR с ключом маски. Клиент должен менять ключ для каждого переданного фрейма. Сервер не должен маскировать свои сообщения. Пример отправки тестового сообщения «Hello world!» сервером:
Fin: True
Reserved: 0x0
Opcode: Text (1)
Mask: False
Payload length: 12
Payload: 48656c6c6f20776f726c6421
Маскировка передаваемых сообщений некриптостойкая, чтобы обеспечить конфиденциальность, для WebSocket следует использовать протокол TLS и схему WSS.
С протоколом разобрались, самое время перейти к CSWSH. Протокол WebSocket использует Origin-based модель безопасности при работе с браузерами. Другие механизмы безопасности, например SOP (Same-origin policy), для WebSocket не применяются. RFC 6455 указывает, что при установке соединения сервер может проверять Origin, а может и нет:
Поле заголовка Origin в рукопожатии клиента означает происхождение скрипта, который устанавливает соединение. Origin сериализуется через ASCII и конвертируется в нижний регистр. Сервер МОЖЕТ использовать эту информацию при принятии решения о том, принимать ли входящее соединение. Если сервер не проверяет Origin, он будет принимать соединение откуда угодно. Если сервер решает не принимать соединение, он ОБЯЗАН вернуть соответствующий номер ошибки HTTP (то есть 403 Forbidden) и отменить рукопожатие по WebSocket, описанное в этой секции.
Уязвимость CSWSH связана со слабой или невыполненной проверкой заголовка Origin в рукопожатии клиента. Это разновидность уязвимости подделки межсайтовых запросов (CSRF), только для WebSocket. Если приложение WebSocket использует файлы cookie для управления сеансами пользователя, злоумышленник может подделать запрос на рукопожатие с помощью атаки CSRF и контролировать сообщения, отправляемые и получаемые через соединение WebSocket.
|
|