Authentication & Security for Real-Time Connections
🔐 Authentication & Security for Real-Time Connections
Authenticating Once, at the Handshake
A typical HTTP API checks auth on every request. A WebSocket authenticates once, at connect time — whatever identity is established during the handshake applies for the entire life of that connection, so getting the handshake right matters more here than it would for any single HTTP endpoint.
| Where to Put the Token | Trade-off |
|---|---|
Query string (?token=...) | Simple, but tokens can leak into server/proxy access logs since they're part of the URL |
| Cookie | Sent automatically on same-origin handshakes (it's still an HTTP request) — reuses the session cookie work from the Authentication course |
| First message after connect | Avoids URL/cookie exposure, but means briefly accepting an unauthenticated connection before validating it |
Rejecting Before the Upgrade Completes
Rejecting inside verifyClient stops a bad handshake before the Chapter 2 Upgrade completes — cheaper than accepting the connection and immediately closing it.
🌍 Origin Checking
The Origin header is present during the handshake, since it's still an HTTP request — checking it against an allowlist stops a malicious page on a different origin from opening a WebSocket to your server using a logged-in user's browser session. This is the exact same threat the CSRF course covers, just aimed at a WebSocket handshake instead of a form submission.
⏱️ Rate Limiting Per Connection
HTTP rate limiting counts requests. A single WebSocket connection can send unlimited messages over the same open socket — limiting needs to happen per connection, not per request:
A Minimal Token Bucket Per Socket
🧪 Validating Every Incoming Message
The same "never trust input" lesson from the SQL Injection and XSS courses applies directly here — JSON.parse'd data is not automatically safe just because it parsed successfully. Validate structure and types before using it:
Trusting the Client vs Not
Naive
Parse, then immediately broadcast whatever came through — no shape check, no auth check beyond the initial handshake, no rate limit.
This Chapter
Authenticated at handshake, origin-checked, rate-limited per connection, and every message validated before it's ever broadcast to anyone else.
💻 Coding Challenges
Challenge 1: Add Origin Checking to verifyClient
Combine the token-check and origin-check examples from this chapter into a single verifyClient function that rejects a connection failing either check, with the correct distinct status code for each failure.
Goal: Practice composing multiple handshake-time checks correctly.
Challenge 2: Validate a Chat Message Shape
Write a validateChatMessage(data) function that returns true only if data has a roomName (non-empty string), a text (non-empty string, max 500 characters), and no other unexpected fields.
Goal: Practice writing a strict shape check rather than trusting a partially-correct message.
Challenge 3: Identify a Stored XSS Vector
A chat client renders every incoming message with messageDiv.innerHTML = data.text. Explain exactly how this becomes a stored XSS vulnerability, referencing the XSS course, and provide the fix.
Goal: Practice recognizing that a WebSocket message is exactly as untrusted as any other user input.
innerHTMLIt's easy to forget, in the middle of building rooms and presence and reconnection logic, that a chat message is still just untrusted user input — exactly the kind the XSS course spent an entire chapter on. If one client sends <img src=x onerror="steal_cookies()"> as their chat text and every other client's UI renders it with innerHTML, that script now runs in every other user's browser the moment the room broadcasts it — a textbook stored XSS attack, just delivered over a WebSocket instead of a database-backed page load. The fix is the same one that course taught: use textContent instead of innerHTML for plain text, or run untrusted HTML through a sanitizer (like DOMPurify) if formatting genuinely needs to be preserved.
🎯 What's Next
Every piece is now in place — the final chapter is a capstone: Building a Real-Time Feature, a live chat application pulling together rooms, authentication, reconnection, and broadcasting from every chapter in this course.