Подтвердить что ты не робот

Для чего нужен ключ Sec-WebSocket?

В разделе 1.3 "Открытие рукопожатия" draft-ietf-hybi-thewebsocketprotocol-17 он описывает Sec-WebSocket-Key следующим образом:

Чтобы подтвердить, что рукопожатие получено, сервер должен взять две части информации и объединить их для формирования ответа. Первая часть информации поступает из | Sec-WebSocket-Key | заголовочное поле в квитировании клиента:

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Для этого поля заголовка сервер должен принять значение (как указано в поле заголовка, например, в кодировке base64 [RFC4648] минус любое начальное и конечное пустое пространство) и объединить это с глобально уникальным идентификатором (GUID, [RFC4122]) "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" в строковой форме, что вряд ли будет использоваться конечными точками сети, которые не понимают протокол WebSocket. Хэш в SHA-1 (160 бит), base64-кодированный (см. Раздел 4 из [RFC4648]), этой конкатенации затем возвращается в квитировании сервера [FIPS.180-2.2002].

Здесь вещь, которую я не могу понять: почему бы просто не вернуть код 101? Если правильное использование Sec-WebSocket-Key предназначено для обеспечения безопасности или для подтверждения того, что они могут обрабатывать запросы websocket, то любой сервер может вернуть ожидаемый ключ, если они захотят, и притвориться, что они являются сервером WebSocket.

4b9b3361

Ответ 1

Согласно стандарту RFC 6455 Websocket

первая часть:

.. the server has to prove to the client that it received the
client WebSocket handshake, so that the server doesn't accept
connections that are not WebSocket connections.  This prevents an
attacker from tricking a WebSocket server by sending it carefully
crafted packets using XMLHttpRequest [XMLHttpRequest] or a form
submission.

...
For this header field, the server has to take the value (as present
in the header field, e.g., the base64-encoded [RFC4648] version minus
any leading and trailing whitespace) and concatenate this with the
Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-
95CA-C5AB0DC85B11" in string form, which is unlikely to be used by
network endpoints that do not understand the WebSocket Protocol.

вторая часть:

The |Sec-WebSocket-Key| header field is used in the WebSocket opening
handshake.  It is sent from the client to the server to provide part
of the information used by the server to prove that it received a
valid WebSocket opening handshake.  This helps ensure that the server
does not accept connections from non-WebSocket clients (e.g., HTTP
clients) that are being abused to send data to unsuspecting WebSocket
servers.

Итак, поскольку значение GUID указано в стандарте, маловероятно (возможно, с очень малой вероятностью), что сервер, который не знает о Websockets, будет использовать его. Он не обеспечивает никакой безопасности (защищенные websockets - wss://- does), он просто гарантирует, что сервер понимает протокол websockets.

Действительно, как вы уже упоминали, если вы знаете о websockets (то, что нужно проверить), вы можете притвориться сервером веб-сервера, отправив правильный ответ. Но тогда, если вы не будете действовать правильно (например, правильно сформировать кадры), это будет считаться нарушением протокола. На самом деле, вы можете написать неверный веб-сервер, но в нем мало пользы.

И еще одна цель - не допустить, чтобы клиенты случайно запрашивали обновление веб-сайтов, не ожидая его (например, добавляя соответствующие заголовки вручную, а затем ожидая smth else). Sec-WebSocket-Key и другие связанные заголовки запрещены для использования с использованием метода setRequestHeader в браузерах.

Ответ 2

В основном для перебора кеша.

Представьте себе прозрачный обратный прокси-сервер, просматривающий HTTP-трафик. Если он не понимает WS, он может ошибочно кэшировать WS-квитирование и отвечать бесполезным 101 на следующего клиента.

Используя nonce (ключ) и требуя базового запроса-ответа, скорее определенного для WS, сервер действительно понял, что это было рукопожатие WS и, в свою очередь, сообщает клиенту, что сервер действительно будет прослушивать порт. Кэширование обратного прокси никогда не будет реализовывать эту логику хэширования "по ошибке".

Ответ 3

Я склонен согласиться.

Ничего важного не изменится, если клиент проигнорировал значение заголовка Sec-WebSocket-Accept.

Почему? Поскольку сервер ничего не доказывает, выполняя этот расчет (кроме этого он имеет код для расчета). Почти единственное, что он исключает, это сервер, который просто отвечает с помощью консервированного ответа.

Обмен заголовками (например, с фиксированными значениями "ключ" и "принятие" ) уже достаточен, чтобы исключить любое случайное соединение с чем-то, что, по крайней мере, не является сервером WebSocket; и, если он пытается, требование, чтобы он выполнял этот расчет, вряд ли препятствует его достижению.

RFC утверждает:

".. сервер должен доказать клиенту, что он получил клиента WebSocket, чтобы сервер не принимал соединения, которые не являются соединениями WebSocket."

и

"Это помогает гарантировать, что сервер не принимает подключения от клиентов, не относящихся к WebSocket.."

Ни одна из этих претензий не имеет никакого смысла. Сервер никогда не отвергает соединение, потому что это тот, который вычисляет хэш, а не тот, который его проверяет.

Такой обмен имел бы смысл, если бы волшебный GUID не был исправлен, но вместо этого был общим секретом между клиентом и сервером. В этом случае обмен позволит серверу доказать клиенту, что он имеет общий секрет, не раскрывая его.

Ответ 4

Что RFC неясно, так это то, что заголовок Sec-WebSocket-Key от клиента должен быть случайным при каждом запросе. Это означает, что любой кэшированный результат от прокси-сервера будет содержать недопустимый заголовок ответа "Sec-WebSocket-Accept", и, следовательно, соединение с веб-сокетом будет прервано вместо непреднамеренного чтения кэшированных данных.

Ответ 5

Спецификация RFC 6455 показывает (минимум) 4 строки, которыми сервер должен ответить клиенту (браузеру). Самое сложное - подтвердить, что ваш код C сервера Websocket делает правильные вычисления. Вот короткий PHP-скрипт (PHP легко установить на все ОС), который будет правильно вычислять ключ для ответа. Жесткий код ключа, который вы получаете от клиента (браузера), во 2-й строке ниже:

<?php
    $client_websocket_key = "IRhw449z7G0Mov9CahJ+Ow==";
    $concat = $client_websocket_key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    $ascii_sha1 = sha1( $concat );  // print this one for debugging, not used for real value.
    $sha1 = sha1( $concat, true );
    echo base64_encode( $sha1 );
?>