9

This question is related to this question I asked

To summarise, I'm playing with websockets at the moment and I'm trying to understand how to authenticate a client connecting to the server using a websocket connection.

On a normal connection, I use token based authentication, I basically just get a token from the server after I log in. Every time I make a request to the server, I put it in a custom header called Authentication which my server reads it from there.

With websockets, this doesn't work because websockets do not have custom headers. I'm left with two options to pass this token.

1) putting the token in the query string - obviously not a great option. Token can be logged by server etc.

2) putting the token in a cookie - this works but only when the client and server sit on the same domain. There's also other restrictions like the client has to be a browser and support cookies etc.

Anyway, the other question is trying to find a solution, this question is about understanding why this is a problem. Why doesn't websockets support custom headers? It's unlikely to be an oversight - websockets and token based authentication are both fairly mature technologies. Is there some sort of security issue with allowing custom headers during the websocket connection?

stickman
  • 1,550
  • 3
  • 13
  • 16

2 Answers2

15

Why doesn't websockets support custom headers? It's unlikely to be an oversight ...

I think this probably is an oversight.

This actually does not surprise me since in my opinion the implementation of WebSockets inside the browser was not well-thought-out in the first place. The most glaring issue is that WebSockets ignore the same origin policy or CORS restrictions, i.e. the server needs to be explicitly check the origin of the connection (Origin header) because otherwise Cross-Site WebSocket Hijacking would be possible.

This insecure-by-default design is in contrast to the secure-by-default behavior of CORS which was created a bit earlier than WebSockets. Given this obvious security problem I doubt that any security considerations played a role in not allowing custom headers as in XHR.

Note that the WebSockets protocol itself supports custom headers since it starts with a HTTP handshake similar to a normal HTTP request. It is only the browser API which is missing the ability to use custom headers.

ruakh
  • 109
  • 2
  • 7
Steffen Ullrich
  • 184,332
  • 29
  • 363
  • 424
  • damn - so in your opinion, what would be the best way to secure a websocket connection on seperate domains? Some sort of use-once only token in the query string? – stickman Nov 27 '17 at 04:05
  • @stickman: This question from the comment is related to your original question but not actually part of it. Please don't ask new question inside a comment but start a new question instead which contains all needed information. – Steffen Ullrich Nov 27 '17 at 04:11
  • thanks, the question in the comment is pretty much what I asked here (https://stackoverflow.com/questions/47495817/how-can-i-authenticate-a-websocket-connection-where-client-and-server-reside-on). So if you got an answer, please answer there i guess. – stickman Nov 27 '17 at 04:27
1

Steffen has answered the question you directly asked, but since the context of your question is "how do you authenticate the client", consider the following: how does any network client (HTTP, SSH, SMTP, RDP, whatever) authenticate?

It's almost always a two step process, which from a very high level abstraction, looks something like this:

  1. Client establishes a connection to the server (technically this step is skippable for protocols built on UDP and other connectionless protocols, but even then there's usually some sort of "client hello" step).
  2. Client sends some authentication data (could be a password, or a token, or an API key, or a public key plus some data signed with the corresponding private key, or...) to the server, possibly in response to some challenge that the server sent over.

Looked at from that perspective, the solution is straightforward. Your WebSocket server accepts all incoming requests (or performs some validation of e.g. origin, but stops short of actually authenticating the client), and then the client transmits its authentication through the websocket. The server doesn't let the socket be used for anything until it receives the authenticating data from the client, and verifies it. If you don't want to store state on the server for each WS, just send the authenticating data (either the original form, or a session token sent back by the client after the initial authentication) along with every message. (That is, after all, what cookies are.)

It'll complicate your WS server code a little. Suddenly you have to distinguish between authenticated and unauthenticated connections, and have a method for switching between them, and disable authenticated functionality while in the unauthenticated state. You have to add new messages, or at least alter the message format, to transport the authentication data. But at the end of the day, it's totally possible. An established websocket is basically just a TCP connection (hopefully tunneled through TLS), and people have been transmitting authentication data (after establishing the connection) over TCP for more than half a century.

CBHacking
  • 40,303
  • 3
  • 74
  • 98