На днях разработчики протокола Web Sockets преподнесли нам небольшой сюрприз под названием редакция 76. Изменения настолько глобальны, что поломана обратная совместимость с редакцией 75. Таким образом, новые клиенты не могут работать со старыми серверами и наоборот. Насколько быстро сломается то, что уже сделано в Web Socket-ном вебе? В принципе уже начало ломаться. Гугловцы
обещали, что WebKit, начиная с ревизии r59903, и Chrome, начиная с версии 6.0.414.0 (ревизия r47952), будет использоваться 76 редакция протокола. А это значит, что нам сейчас надо срочно обновлять наши сервера.
Давайте рассмотрим, что именно изменено и ради чего такая спешка, что даже потребовалось ломать рабочие версии.
Основные изменения связаны с безопасностью подключения. Цель: предотвратить или хотя бы максимально осложнить подделку запросов, отправленных с неверных источников. Например, чтобы нельзя было послать из браузера AJAX-запрос максимально похожий на установление веб-сокетного соединения и тем самым атаковать веб-сокет сервер.
Для этого в протокол был добавлен целый ряд заголовков, начинающихся на sec-.
Сравним старый и новый запрос на установление подключения по Web Socket:
Старый запрос:
GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin:
http://example.com WebSocket-Protocol: sample
Новый:
GET /demo HTTP/1.1
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 1_ tx7X d < nw 334J702) 7]o}` 0
Sec-WebSocket-Protocol: sample
Upgrade: WebSocket
Sec-WebSocket-Key1: 18x 6]8vM;54 *(5: { U1]8 z [ 8
Origin:
http://example.com ^n:ds[4U
Заголовок Sec-WebSocket-Protocol указывает какой субпротокол вы планируете использовать. Название - ваше собственное, можно использовать, например, chat или sample или еще какое-нибудь. Данный заголовок позволяет проверить, что и сервер, и клиент поддерживают ваш протокол. Если это не так, то соединение будет сброшено. Это немного упрощает проверку поддержки конкретного протокола или его версии, экономит вам несколько строк кода.
За собственно безопасность отвечают два заголовка-близнеца Sec-WebSocket-Key1 и Sec-WebSocket-Key2. Обрабатываются они одинаково: из полученного мусора символов извлекаются цифры. Мы должны получить такой результат:
Key1: 1868545188
Key2: 1733470270
Одновременно подсчитывается количество пробелов в каждом ключе. В данном случае 12 и 10. Каждое число делится на соответственное количество пробелов. В данном случае мы получим два 32-битных числа:
Key1: 155712099
Key2: 173347027
Но и это еще не все. Дальше идет нечто совершенно безумное, похожее на тело GET-запроса. Страшно писать такие слова...
Два полученных числа представляются как big endian 32-битные целые и затем "слепляются" с образование 8 байт. Затем туда же пристыковываются 8 байт из "тела запроса". Вот эти 16 байт и образуют "секретную" подпись. Теперь задача сервера правильно ответить на такой запрос.
Опять сравним старый ответ:
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
WebSocket-Origin:
http://example.com WebSocket-Location: ws://example.com/demo
WebSocket-Protocol: sample
и новый:
HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin:
http://example.com Sec-WebSocket-Location: ws://example.com/
Sec-WebSocket-Protocol: chat
fQJ,fN/4F4!~K~MH
Как мы видим после заголовков ответ сервера содержит еще какие-то данные - это та самая "секретная" подпись, от которой взят md5. Полученные 128 бит записываются как есть, без конвертаций в hex-, dec- или еще куда-то.
Оригинал:
Новый протокол Web Sockets