WebSocket Connected — but Is the Connection Still Secure?
A 101 Switching Protocols response only proves the channel opened. Audit Origin, session lifetime, message authorization, limits, backpressure, and logging for WebSockets.
WebSocket Connected — but Is the Connection Still Secure?
The browser opens wss://app.example.com/socket, the server replies with 101 Switching Protocols, and the interface starts receiving live updates. Functionally, everything looks correct.
After the upgrade, however, many familiar HTTP assumptions no longer apply in the same way. The connection can remain open for minutes or hours. Individual messages are no longer ordinary HTTP requests. A user may log out, lose a role, or be removed from a workspace while the socket itself continues to exist.
The useful audit question is therefore not: “Does WebSocket work?” It is: “Do origin, identity, authorization, and resource limits remain correct for the entire lifetime of the connection?”
The current WHATWG WebSockets Living Standard, last updated on 15 March 2026, integrates the browser handshake with Fetch so cookies, HSTS, and related browser rules are handled consistently. RFC 6455 also defines the Origin model for browser connections. OWASP highlights the additional controls required because the connection persists long after the initial handshake.
1. Origin is a real browser security boundary for WebSockets
A common misconception is: “Our API has CORS, so WebSockets are covered too.”
That is not a safe assumption.
Browsers send an Origin header during the WebSocket handshake. RFC 6455 explicitly describes this as a defense against unauthorized cross-origin WebSocket use by browser scripts. If your socket is intended only for https://app.example.com, the server should compare the supplied origin with a small explicit allowlist and reject unacceptable origins during the handshake.
Why this matters: the WHATWG algorithm uses credentials mode include for the handshake. Depending on cookie scope and policy, existing authentication credentials can therefore participate in the connection. If a server also accepts arbitrary origins, a hostile website can attempt to open your WebSocket from the browser of a user who is already signed in. OWASP describes this class of attack as Cross-Site WebSocket Hijacking.
Red flags include:
- accepting every
Origin, - reflecting the incoming origin without validation,
- substring checks such as “contains example.com,”
- permanently sharing one allowlist across preview, staging, and production,
- silently accepting a missing
Originon the browser-facing endpoint.
There is an important limit to this control. RFC 6455 also notes that non-browser clients can forge an Origin value. Origin checking is not authentication. It protects the browser boundary; it does not prove that every client is trustworthy.
2. The handshake authenticates a connection — not every future permission
Many implementations validate a session or token during the upgrade and then treat the socket as authorized for the rest of its lifetime.
That is fragile.
Consider this timeline:
- A user connects at 09:00 as an administrator.
- At 09:15, their role is reduced to read-only.
- The socket remains open until 12:00.
- Messages such as
deleteProjectorexportCustomersare still authorized using the permissions captured when the connection opened.
The HTTP session may have changed correctly while the real-time connection remains frozen in the past.
Long-lived sockets therefore need an explicit policy:
- When does the underlying session expire?
- Is it revalidated server-side while the connection remains open?
- What happens on logout?
- What happens after password reset, account suspension, or role change?
- Can administrators revoke active connections?
- Which permissions are evaluated again for each message?
OWASP recommends binding long-running connections to the actual session lifecycle and closing them when the session expires or the user logs out.
3. Authorization belongs on the message, not only on the socket
A WebSocket is not a permanent master key.
Suppose a message looks like {"type":"project.delete","projectId":"4711"}. Even when the connected user is authenticated, the server still needs to answer:
- May this user delete projects?
- Does project 4711 belong to their workspace?
- Is the project currently in a state where deletion is permitted?
- May this exact user perform this exact action now?
The same rule applies to subscriptions. A client must not be able to subscribe to tenant:other-company or document:secret-id merely because the protocol accepts an arbitrary string.
This is particularly important in multi-tenant SaaS. The socket may know userId=123, but every resource still needs object- and tenant-level authorization.
Authenticated means: we know who is connected.
Authorized means: that person may perform this specific action on this specific object.
Those statements remain separate inside a WebSocket protocol.
4. WebSocket messages are untrusted input
HTTP goes away after the upgrade, but input validation does not.
Every inbound message can be manipulated. A robust handler validates at least:
- allowed message type,
- schema,
- required fields and data types,
- maximum string and array lengths,
- object and tenant ownership,
- permitted state transitions,
- allowed frequency,
- maximum message size.
Avoid dispatchers where the client sends an arbitrary method name and the server dynamically calls a matching internal function. A small explicit command map is easier to review and test.
Error messages should also be controlled. Internal stack traces, SQL errors, file paths, and complete validation internals should not be reflected to the client.
5. An open connection is a resource
WebSockets are deliberately persistent. That is exactly why they need limits.
OWASP recommends connection and message limits, timeouts, and heartbeats. MDN also warns that the classic WebSocket API has no built-in backpressure. If messages arrive faster than an application can process them, memory and CPU pressure can grow quickly.
A production review should therefore ask:
- How many concurrent connections may one account open?
- Are there sensible additional limits per IP or network?
- How large may one message be?
- How many messages are accepted per time window?
- What happens when a client produces work faster than the server can process it?
- Are idle connections closed?
- Do ping/pong frames or application heartbeats detect dead peers?
- Are slow or stuck consumers disconnected predictably?
- Are queue and buffer limits observable?
A 101 handshake is cheap. Tens of thousands of permanently active connections may not be.
6. Tokens in WebSocket URLs are convenient — and tend to appear in logs
The browser's WebSocket() API does not let application code freely add arbitrary HTTP headers to the handshake. Some systems therefore put authentication tokens in URL query parameters.
That can work technically, but it creates another exposure path: URLs may appear in reverse-proxy, load-balancer, CDN, error, or application logs.
OWASP recommends treating query-string tokens carefully and redacting them from logs. Depending on the architecture, safer choices include securely scoped cookie sessions or a deliberately designed authentication message immediately after the connection opens, with short-lived credentials and clear failure handling.
If query tokens are unavoidable, they should at minimum be short-lived, single-use or tightly bound where practical, and masked at every logging layer.
7. Proxies and monitoring must still understand the connection after the upgrade
Traditional HTTP access logs often show only:
GET /socket → 101 Switching Protocols
Ten thousand messages can flow afterwards without ordinary request logs explaining what happened.
A WebSocket review therefore needs dedicated observability. Useful fields can include:
- connection ID,
- user or pseudonymous account ID,
- authenticated role or permission version,
- accepted origin,
- negotiated subprotocol,
- connection duration,
- close code and reason,
- counts or sizes of inbound and outbound messages,
- rate-limit events,
- authorization failures,
- reconnect loops,
- server and proxy disconnects.
Do not simply log every payload. Chats, document contents, tokens, and personal data can turn debug logging into its own security and privacy problem.
A practical WebSocket audit
Test 1: Foreign browser origin
Use a controlled page on another origin and attempt to open the production socket. An existing login session must not cause the server to accept the hostile origin.
Test 2: Logout while connected
Open the socket, then sign out in a second tab. Verify that the connection is closed or, at minimum, that the next privileged message fails server-side.
Test 3: Role change
Open a connection with elevated rights, reduce the role on the server, then send a privileged message. The connection must not preserve obsolete permissions indefinitely.
Test 4: Tenant and object boundaries
Manipulate IDs and subscription names. A user from workspace A must not subscribe to or mutate resources in workspace B.
Test 5: Flood and oversized messages
Send many small messages and individual oversized messages. Observe limits, memory, CPU, backpressure, and controlled disconnect behavior.
Test 6: Network interruption
Drop the network, switch interfaces, or restart the proxy. Verify heartbeat behavior, reconnect logic, duplicate subscriptions, and whether actions are accidentally repeated.
Test 7: Logging
Search CDN, proxy, application, and error logs for session identifiers, tokens, query parameters, and complete message payloads.
What Website-Pflichtencheck would inspect
A technical WebSocket review can follow the real upgrade path from browser through CDN and reverse proxy to the socket server. Depending on the application, it can cover Origin allowlisting, TLS via wss, session and token design, logout and permission changes, object-level authorization, message schema and size limits, rate limiting, connection limits, heartbeats, backpressure, subprotocol negotiation, reconnect behavior, logging, and monitoring.
The goal is not to replace WebSockets with polling. For chat, collaboration, live dashboards, and status updates, persistent bidirectional communication is often exactly the right tool.
But a WebSocket is not a tunnel that becomes trusted forever because the handshake succeeded once. Security has to survive from the opening request to the final message.
A 101 Switching Protocols response says the channel is open. A resilient audit answers the more important question: Who may do what through that channel — and is the answer still correct two hours later?