In this article, we will dive into the concept of WebSocket introduced in HTML 5, security issues around the WebSocket model, and the best practices that should be adopted to address security issues around WebSocket. Before going straight to security, let’s refresh our concepts on WebSocket.
Why Websocket and Not HTTP?
In older times, the client server model was built with client requests the server for a resource. The Web was built for this kind of model, and HTTP was sufficient to handle these requests. However, with new advancements of technologies, needs of online gaming and real time applications have marked the need of a protocol that could provide a bidirectional connection between client and server to allow live streaming.
Web applications have grown up a lot, and are now consuming more data than ever before. The biggest thing holding them back was the traditional HTTP model of client initiated transactions. To overcome this, a number of different strategies were devised to allow servers to push data to the client. One of the most popular of these strategies was long-polling. This involves keeping an HTTP connection open until the server has some data to push down to the client.
The problem with all of these solutions is that they carry the overhead of HTTP. Every time you make an HTTP request, a bunch of headers and cookie data are transferred to the server. Initially HTTP was thought to be modified to create a bidirectional channel between client and server, but this model could not sustain because of the HTTP overhead and would certainly introduce latency. But in real time applications, especially gaming applications, latency cannot be afforded. Because of this shortcoming of HTTP, a new protocol known as WebSocket, which runs over the same TCP/IP model, was designed.
How WebSockets Work
WebSockets provide a persistent connection between client and server that both parties can use to start data at any time. The connection is initiated from client through a WebSocket handshake. This happens over a normal HTTP request packet with an “Upgrade” header. A sample connection is shown below:
Later, if the server supports the WebSocket connections, then the server responds with an “Upgrade” header in the response. Sample is below:
After an exchange of these request and response messages, a persistent WebSocket connection is established between a client and a server.
WebSockets can transfer as much data as you like without incurring the overhead associated with traditional HTTP requests. Data is transferred through a WebSocket as messages, each of which consists of one or more frames containing the data you are sending (the payload). In order to ensure the message can be properly reconstructed when it reaches the client, each frame is prefixed with 4-12 bytes of data about the payload. Using this frame-based messaging system helps to reduce the amount of non-payload data that is transferred, leading to significant reductions in latency.
Note: the “Upgrade” header tells the server that the client wants to initiate a WebSocket connection.
WebSocket Security Issues
WebSocket has some inherent security issues. Some of them are listed below:
Open to DOS attack: WebSocket allows unlimited number of connections to the target server and thus resources on the server can be exhausted because of DOS attack.
The WebSocket protocol does not give any particular way to allow a server to authenticate the clients during the handshake process. WebSocket has to take forward only the mechanism available to normal HTTP connections such as cookies, HTTP authentication or TLS authentication. It has been seen that during the upgrade handshake from HTTP to WebSocket (WS), HTTP sends all the authentication information to WS. This attack has been termed as Cross Site WebSocket Hijacking (CSWSH).
WebSockets can be used over unencrypted TCP channels, which can lead to major flaws such as those listed in OWASP Top 10 A6-Sensitive Data Exposure.
WebSockets are vulnerable to malicious input data attacks, therefore leading to attacks like Cross Site Scripting (XSS).
The Websocket protocol doesn’t handle authorization and/or authentication. Application-level protocols should handle that separately in case sensitive data is being transferred.
- It’s relatively easy to tunnel arbitrary TCP services through a WebSocket, for example, tunnel a database connection directly through to the browser. This is of high risk, as it would enable access to services to an in-browser attacker in the case of a Cross Site Scripting attack, thus allowing an escalation of a XSS attack into a complete remote breach.
Recommendations around WebSockets Security flaws
Below are the recommendations / best practices around the security flaws listed above:
The WebSocket standard defines an Origin header field which Web browsers set to the URL that originates a WebSocket request. This can be used to differentiate between WebSocket connections from different hosts, or between those made from a browser and some other kind of network client. However, the Origin header is essentially advisory: non-browser clients can easily set the Origin header to any value, and thus “pretend” to be a browser. Origin headers are roughly analogous to the X-Requested-With header used by AJAX requests. Web browsers send a header of X-Requested-With: XMLHttpRequest which can be used to distinguish between AJAX requests made by a browser and those made directly. However, this header is easily set by non-browser clients, and thus isn’t trusted as a source of authentication. Use session-individual random tokens (like CSRF-Tokens) on the handshake request and verify them on the server.
Websockets must be configured to use secure TCP channel. URI with syntax wss:// illustrates the usage of secure Websocket connection.
Any data from untrusted sources must not be trusted. All input must be sanitized before it goes in the execution context.
You should apply equal suspicion to data returned from the server as well. Always process messages received on the client side as data. Don’t try to assign them directly to the DOM, nor evaluate as code. If the response is JSON, always use JSON.parse() to safely parse the data.
- Avoid tunneling if at all possible, instead developing more secured and checked protocols on top of WebSockets.