Episode ini membahas mengamankan WebSocket di production: transport TLS dengan wss, keamanan token, validasi input, proteksi dari CSWSH dan DoS, konfigurasi CORS, serta security header.

WebSocket membuka saluran dua arah langsung ke server kalian — dan pintu yang terbuka dua arah juga bisa dimasuki penyerang dua arah. Pesan yang tidak divalidasi bisa menjadi XSS, origin yang tidak dicek bisa dibajak, dan koneksi tanpa TLS bisa disadap.
Episode 19 merangkum security best practices: melindungi transport, mengamankan autentikasi, memvalidasi semua input, menangkal serangan khusus WebSocket seperti CSWSH, dan memasang security header.
WebSocket tanpa enkripsi menyebarkan semua pesan polos di jaringan. Di production, tidak ada alasan memakai ws tanpa TLS.
const https = require("https");
const fs = require("fs");
const { WebSocketServer } = require("ws");
const server = https.createServer({
cert: fs.readFileSync("/etc/ssl/cert.pem"),
key: fs.readFileSync("/etc/ssl/key.pem"),
});
const wss = new WebSocketServer({ server });https.createServer({ cert, key }) membungkus WebSocket dengan TLS. Klien lalu memakai wss:// dan handshake upgrade terjadi di atas koneksi terenkripsi.
Arahkan semua traffic HTTP biasa ke HTTPS, dan pasang HSTS agar browser tidak pernah mencoba HTTP lagi.
Strict-Transport-Security: max-age=31536000; includeSubDomainsHeader Strict-Transport-Security memberi tahu browser untuk selalu memakai HTTPS ke domain itu selama satu tahun. Ini memblokir serangan downgrade dan man-in-the-middle.
Token autentikasi untuk WebSocket harus pendek umurnya. JWT dengan masa berlaku berjam-jam memperbesar jendela penyalahgunaan jika bocor. Kombinasi yang baik: akses token singkat untuk koneksi WebSocket, refresh token panjang yang hanya dipakai saat login ulang.
Selalu periksa header Origin saat handshake untuk mencegah serangan CSWSH.
const { WebSocketServer } = require("ws");
const wss = new WebSocketServer({
port: 8080,
verifyClient: (info) => {
return info.origin === "https://app.kalian.com";
},
});verifyClient(info) memeriksa info.origin sebelum koneksi diterima. Hanya origin terpercaya yang boleh terhubung — ini pertahanan pertama melawan cross-site hijacking.
Setiap pesan yang masuk harus dianggap berbahaya sampai terbukti aman.
wss.on("connection", (ws) => {
ws.on("message", (data) => {
let pesan;
try {
pesan = JSON.parse(data.toString());
} catch {
ws.close(1008, "format tidak valid");
return;
}
if (typeof pesan.teks !== "string" || pesan.teks.length > 500) {
ws.close(1008, "pesan terlalu panjang");
return;
}
simpanDanKirim(escapeHTML(pesan.teks));
});
});Validasi tipe dan panjang sebelum memproses, dan sanitasi output dengan escapeHTML untuk mencegah XSS saat pesan dirender browser. Prinsipnya: terima minim, validasi ketat.
CSWSH terjadi saat halaman jahat memaksa browser pengguna membuka WebSocket ke server yang dipercaya. Karena cookie ikut terkirim, server mengira itu koneksi sah. Pertahanannya berlapis:
Server yang tanpa batas mudah dilumpuhkan.
const wss = new WebSocketServer({
port: 8080,
maxPayload: 64 * 1024,
clientTracking: true,
});
wss.on("connection", (ws) => {
if (wss.clients.size > 5000) {
ws.close(1013, "server penuh");
}
});maxPayload: 64 * 1024 membatasi ukuran pesan, dan cek wss.clients.size > 5000 menolak koneksi saat kapasitas penuh. Rate limiting dari episode 13 menambah lapisan berikutnya.
CORS berlaku saat handshake. Karena WebSocket tidak bisa mengirim header custom di browser, validasi Origin adalah bentuk kontrol utama — pastikan daftar origin yang diizinkan dikelola secara eksplisit, bukan wildcard.
Tambahkan header dasar di server HTTP yang melayani halaman aplikasi:
Content-Security-Policy: default-src 'self'; connect-src 'self' wss://api.kalian.com
X-Frame-Options: DENY
X-Content-Type-Options: nosniffContent-Security-Policy dengan connect-src secara eksplisit mengizinkan koneksi WebSocket ke domain tertentu dan memblokir lainnya. Ini membatasi kerusakan jika terjadi XSS.
Episode 19 menutup celah paling umum aplikasi WebSocket: transport tanpa enkripsi, token yang mudah disalahgunakan, input yang tidak divalidasi, dan koneksi dari origin tak dikenal.
Inti yang harus dibawa pulang:
Di episode 20 berikutnya kita membahas real-time collaboration features: Operational Transformation, CRDT, editing kolaboratif, serta library seperti Yjs dan ShareDB.