WebSocket — це мережевий протокол поверх TCP, створений для двонаправленого обміну повідомленнями між браузером та сервером через постійне з'єднання. На відміну від класичного HTTP, де кожен запит відкриває та закриває з'єднання, WebSocket підтримує канал, яким клієнт і сервер можуть обмінюватися даними без повторних рукостискань. Сьогодні стандартизація API та специфікації протоколу активно ведеться в організаціях стандартизації — таких як W3C та IETF — а підсумкові документи відображають практики, підтверджені експертами та розробниками галузі.
Відкриття каналу WebSocket
Установка WebSocket-з'єднання виконується через процедуру, що нагадує HTTP-хандшейк: клієнт відправляє спеціальний запит, сервер відповідає відповідними заголовками, після чого TCP-з'єднання перемикається в режим WebSocket. В історії протоколу було кілька ключових редакцій — кожна вносила значні зміни до безпеки, структури повідомлень та сумісності.
Протокол 75
У ранній редакції (архівні матеріали кінця 2009 - середини 2010) процедура встановлення була максимально простою. Клієнт відправляє запит, схожий на HTTP GET із заголовками Upgrade та Connection, а сервер повертає відповідь із кодом 101, що підтверджує «перехід» на WebSocket. Після такого обміну канал вважався відкритим, і розпочався двонаправлений обмін тим самим TCP-з'єднанням.
Приклад запиту клієнта (спрощено):
GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample
У відповідь сервер надсилав:
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
Після підтвердження з'єднання обмін текстовими повідомленнями (UTF-8) вимагав спеціальних обмежувачів: нульовий байт перед повідомленням та байт 255 після нього — підхід, характерний для ранніх чорнових реалізацій.
Протокол 76
Редакція 76, прийнята в середині 2010 року, запровадила механізми захисту від підробки запитів та змінила процедуру рукостискання, втративши зворотну сумісність із найбільш ранніми реалізаціями. Тепер у запиті з'явилися випадково згенеровані заголовки Sec-WebSocket-Key1 та Sec-WebSocket-Key2 та восьмибайтове тіло; сервер у відповідь повертав змінені заголовки Sec-WebSocket-Origin, Sec-WebSocket-Location, Sec-WebSocket-Protocol та 16-байтне тіло, розраховане за певним алгоритмом.
Нові заголовки додавалися клієнтом випадково. Аналіз та перевірка цих полів робилися на серверній стороні, що підвищувало стійкість до простих атак.
Алгоритм формування 16-байтного тіла відповіді (спрощена схема):
- Прибрати з рядка значення Sec-WebSocket-Key1 усі символи, які не є цифрами.
- Отримати 64-бітове число з цифр, що залишилися.
- Розділити це число цілочислово на кількість прогалин у вихідному рядку.
- Уявити результат як 4-байтове число у форматі big-endian.
- Повторити для Sec-WebSocket-Key2.
- Конкатенувати два 4-байтові рядки та додати 8-байтове тіло запиту.
- Обчислити MD5 від отриманого 16-байтового рядка і відправити цей бінарний MD5 у тілі відповіді «як є».
Важливо: хоча хендшейк виглядає як HTTP, він не повністю дотримується всіх угод HTTP (наприклад, тіло запиту є при відсутності Content-Length), тому сервери повинні були розрізняти версії за наявності заголовків Sec-WebSocket-Key1/Sec-WebSocket-Key2 та коректно підтримувати обидва типи клієнтів.
Протокол 07
Редакція 07 (квітень 2011) змінила формат кадрів, що передаються, і ввела обов'язкове маскування даних від клієнта до сервера. У цій версії кожне повідомлення клієнта маскується унікальною 4-байтовою маскою, яка застосовується до кожного байта корисного навантаження; це вирішувало низку проблем із проміжними проксі та коректністю передачі.
Повідомлення стало забезпечуватися заголовком, що містить прапори, що управляють, і метадані:
- прапор фрагментації повідомлення (чи є фрагменти);
- тип корисних даних (текст, бінарні дані тощо);
- ознака маскування та сама маска;
- довжина даних (включаючи розширення для великих повідомлень);
- службові опкоди для управління (ping, pong, close та ін.).
Приклад клієнтського запиту для версії 07 (скорочено):
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 7
Сервер у відповідь надсилає:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Заголовок Sec-WebSocket-Accept формується так:
- Поєднати значення Sec-WebSocket-Key з фіксованим рядком 258EAFA5-E914-47DA-95CA-C5AB0DC85B11.
- Обчислити бінарний SHA-1 хеш від цього рядка (20 байт).
- Закодувати результат у Base64 — це буде значення Sec-WebSocket-Accept.
Приклад на PHP (спрощено):
<?php
echo base64_encode(sha1("dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true));
?>
Протокол RFC 6455 (фрейми)
11 грудня 2011 року специфікація остаточно стала RFC 6455. У цій версії були закріплені правила передачі даних фреймами, обов'язкове маскування клієнтських повідомлень, формат заголовків та обробка опкодів, що управляють. Також було прийнято використовувати в заголовках стандартне ім'я Origin замість попередніх варіацій. Починаючи з цієї редакції, взаємодія між клієнтом та сервером по WebSocket стала передбачуваною та сумісною між основними реалізаціями.
Схема URI
Протокол визначає дві основні URI-схеми:
- ws: для нешифрованих WebSocket з'єднань;
- wss: для зашифрованих (TLS/SSL) з'єднань.
Використання wss: рекомендується для публічних та критичних сервісів, оскільки шифрування захищає від заміни та перегляду трафіку посередниками.
Реалізація WebSocket у браузерах
На стороні клієнта у браузері створюється об'єкт WebSocket, до конструктора якого передається URI сервера, і підписуються обробники подій — відкриття з'єднання, отримання повідомлення, помилка та закриття. Типовий приклад виглядає просто: створити об'єкт, відправити повідомлення під час відкриття та обробити вхідні повідомлення.
Мінімальний приклад (логіка, спрощено):
const webSocket = новий WebSocket('ws://localhost/echo');
webSocket.onopen = event => {
alert('onopen');
webSocket.send("Hello Web Socket!");
};
webSocket.onmessage = event => {
alert('onmessage, ' + event.data);
};
webSocket.onclose = event => {
alert('onclose');
};
Підтримка WebSocket реалізована у всіх сучасних двигунах браузерів. На етапі впровадження деякі версії мали експериментальну або частково відключену підтримку через безпеку, але зі стандартом RFC 6455 сумісність і поведінка вирівнялися.
- Google Chrome — підтримка з ранніх версій (серії 4.x у чорнових білдах);
- Apple Safari — підтримує WebSocket, починаючи з версій 5.x;
- Mozilla Firefox — підтримка стабільна з версій 4 та вище;
- Opera - реалізував підтримку у версіях 10.7x і вище;
- Internet Explorer — підтримка з'явилася у версіях 10 та вище.
Для оцінки підтримки конкретних функцій WebSocket у різних браузерах зручно звірятись із ресурсами сумісності, такими як caniuse.com, а також з офіційною документацією браузерних вендорів та замітками експертів.
У листопаді 2010 року дослідження, опубліковане Adam Barth, показало можливі проблеми надійності та безпеки при загальному використанні WebSocket в умовах прозорих проксі — можлива була заміна кешу та втручання у трафік. Ці результати вплинули на рішення розробників браузерів за умовчанням тимчасово обмежувати або ретельно перевіряти підтримку WebSocket до усунення вразливостей. Сьогодні з урахуванням RFC 6455 та застосування TLS більшість проблем вирішено, але рекомендації з безпеки залишаються актуальними.
| Версія протоколу | Рік / Дата | Ключові зміни | Handshake тіло (байт) | Маскування |
|---|---|---|---|---|
| 75 | 2009–2010 | Простий хендшейк, текстові обмежувачі (0x00/0xFF) | ні | ні |
| 76 | червень 2010 | Sec-WebSocket-Key1/Key2, MD5-відповідь (захист від підробки) | запит 8, відповідь 16 | ні |
| 07 | квітень 2011 | Фрейми, обов'язкова 4-байтна маска від клієнта | ні (використовуються заголовки та опкоди) | обов'язково для клієнта → сервера |
| RFC 6455 | 11 груд. 2011 | Стандартизація формату кадрів, опкоди, Origin | ні | обов'язково для клієнта → сервера |
Цікаві факти та поради про WebSocket
- WebSocket заощаджує мережний трафік та затримки: постійне з'єднання усуває накладні витрати на встановлення TCP та TLS при кожному повідомленні.
- Для публічних сервісів використовуйте wss: це обов'язковий крок для захисту від MITM та витоку даних – практика, підтверджена рекомендаціями фахівців з безпеки.
- Якщо потрібно масштабування, використовуйте спеціалізовані проксі та шлюзи (наприклад, балансувальники WebSocket‑aware), які коректно працюють із тривалими з'єднаннями та підтримують розриви/перепідключення.
- Тестуйте поведінку через реальні проксі та мобільні мережі: деякі прозорі проксі та мережні пристрої можуть закривати або модифікувати WebSocket-сесії.
- Логи та моніторинг кадрів важливіші, ніж просто логування HTTP-запитів: WebSocket має керуючі опкоди (ping/pong/close), які допомагають діагностувати стан з'єднання.
Часті питання
Що таке WebSocket і чим він відрізняється від HTTP?
WebSocket - протокол для двонаправленого обміну даними поверх одного постійного TCP-з'єднання. На відміну від HTTP, де кожна дія зазвичай ініціюється клієнтом і вимагає окремого запиту, WebSocket дозволяє серверу і клієнту обмінюватися повідомленнями в будь-який час на одному відкритому канал
Чи потрібне мені шифрування для WebSocket?
Для державних і приватних послуг рекомендуємо використовувати wss: (WebSocket через TLS). Шифрування захищає від перехоплення і замінників повідомлень, особливо при роботі через ненадійні мережі або спільні проксі
Що таке маскування в WebSocket?
Маскування - необхідний механізм для повідомлень клієнтів, де корисне навантаження XOR походить від 4-байтової маски. Це знижує ризик втручання з боку деяких типів проміжних пристроїв і запобігає певним категоріям ата
Як перевірити WebSocket у своєму браузері?
Підтримка перевіряється як властивість об'єкта WebSocket у сценарії (наприклад, 'WebSocket' у вікні) і через інформацію про версії в посібниках сумісності, таких як canius om; також розглянемо поведінку конкретної версії браузера та движк
