A WebSocket Subprotocol Is Negotiation, Not Authorization
A WebSocket Subprotocol Is Negotiation, Not Authorization The WebSocket opening handshake borrows fields from HTTP, including Sec-WebSocket-Protocol. A client lists application-level protocols it can speak, and the ser
A WebSocket Subprotocol Is Negotiation, Not Authorization
The WebSocket opening handshake borrows fields from HTTP, including Sec-WebSocket-Protocol. A client lists application-level protocols it can speak, and the server selects one in the response. RFC 6455 defines this as negotiation. It does not define an authentication mechanism, a permission model, or a trusted tenant boundary. Trouble begins when a gateway treats a selected subprotocol string as proof that the caller belongs to a particular backend or role.
Imagine a collaboration service with two subprotocols, chat.v1 and admin.v1. The edge chooses the backend route from the requested value. Both routes share a session cookie. The admin handler checks a role after the upgrade, but a shared connection registry or message bus authenticates only the connection identifier. A user who can open admin.v1 may land on a connection object whose downstream consumers assume the role was checked. The subprotocol did not grant access by itself. It routed the request into a component with weaker checks.
The handshake carries an Origin header, but origin is not a credential. It can be useful as one policy input for browser clients, while non-browser clients can set it freely. The server must authenticate the connection with a session, token, certificate, or another mechanism it actually verifies. It should authorize the requested resource before accepting the upgrade and then authorize each privileged action again at the point where the action is executed.
Subprotocol selection needs a deny-by-default list. If the value is absent, choose only a default protocol that has the same authorization requirements as every other path. If it is unknown or duplicated, reject the upgrade. If negotiation selects a protocol based on a header that the application does not otherwise trust, record the choice in the audit event. Do not let a proxy rewrite the list without updating the security model.
Long-lived connections create a state problem. A user may be removed from a workspace while a socket remains open. The connection may continue to receive events or send messages until the next token check. Use short-lived connection credentials, periodic authorization refresh, and server-side revocation events for session closure. For high-risk actions, require a new request-level authorization decision even on an established socket. A validated connection identity is not a permanent grant.
Message framing also needs an application policy. RFC 6455 provides data and control frames, but it does not impose business-level size, rate, or schema limits. Set a maximum message size before allocation, validate JSON or binary payloads against a schema, and apply per-user and per-connection quotas. Put authorization checks on the action extracted from the message as well as on the socket's initial path.
Testing should include a valid connection with an invalid subprotocol, a valid subprotocol with a low-privilege user, and a privilege change during the connection. Verify that the server rejects unknown values before route selection and that downstream services do not trust a subprotocol claim as an identity. Check whether the proxy and application disagree about duplicate or case-varied header values.
WebSockets are useful because they keep a stateful channel open. That state should be treated as a cache of identity and authorization, not as a replacement for either. Negotiation can pick a protocol, but the application must still decide who may connect and what each message may do.
References
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.