Skip to content

Instantly share code, notes, and snippets.

@subudeepak
Last active November 2, 2022 00:04
Show Gist options
  • Save subudeepak/9897212 to your computer and use it in GitHub Desktop.
Save subudeepak/9897212 to your computer and use it in GitHub Desktop.
The problems and some security implications of websockets - Cross-site WebSockets Scripting (XSWS)

WebSockets - An Introduction

WebSockets is a modern HTML5 standard which makes communication between client and server a lot more simpler than ever. We are all familiar with the technology of sockets. Sockets have been fundamental to network communication for a long time but usually the communication over the browser has been restricted. The general restrictions

  • The server used to have a permanent listener while the client (aka browser) was not designated any fixed listener for a more long term connection. Hence, every communication was restricted to the client demanding and the server responding.
  • This meant that unless the client requested for a particular resource, the server was unable to push such a resource to the client.
  • This was detrimental since the client is then forced to check with the server at regular intervals. This meant a lot of libraries focused on optimizing asynchronous calls and identifying the response of asynchronous calls. Notably the most common approach was to create on the fly functions with different names as callback functions. This can be witnessed here. If you open your console log and observe the network, you would notice how many connections are being made for this task. Pinging the server is not the problem but the amount of work involved in setting up a new connection everytime is. This is however essential in the old HTML standard to ensure that the client is never directly attacked by anyone.

Modern technologies have made it possible to sandbox the browser on a whole new scale thereby making it possible for riskier modes of access to be made. Hence a lot of newer technologies have been introduced including WebSockets. WebSockets is slightly different than the standard socket implementation. In this case, the protocol is still http[s] and this makes it more versatile (it has no problems in working with http proxies). The protocol is itself symbolized by ws[s]:// and it keeps the established connection open to continuously send or receive messages. WebSockets is an important and interesting protocol whose importance cannot be understated. I think that a growing number of web developers will try to implement WebSockets into their websites and it is going to be an undeniably dominant technology in the years to come.

I think that a growing number of web developers will try to implement WebSockets into their websites and it is going to be an undeniably dominant technology in the years to come.

An example implementation for WebSockets in JavaScript

var wsUri = "wss://" + document.location.host + "/wsendpoint";
try
{
    var websocketExample = new WebSocket(malwsUri);
}
catch(err)
{
    onError(err);
}
websocketExample.onerror = function(evt) { onThisError(evt); };
websocketExample.onmessage = function(evt) { onThisMessage(evt); };
function onThisError(evt) {
    writeToThisScreen('ERROR:' + evt.data);   
}
websocketExample.onopen = function(evt) { onThisOpen(evt); };
function writeToThisScreen(message) {
    var output = document.getElementById("output");
    output.innerHTML += '<div><p>'+message+'</p></div>';
}
function onThisOpen() {
    writeToThisScreen("Connected to " + malwsUri);
    websocketExample.send('hello');
    
}
function onThisMessage(evt) {
    writeToThisScreen("Received from server: " + evt.data);
}
function closeThisConnection()
{
    websocketExample.close();
}
function sendThisMessage()
{
    websocketExample.send(document.getElementById('message').value);
}
document.addEventListener('DOMContentReady', function () { document.getElementById('sendMsg').addEventListener('click', websocketExample.send(document.getElementById('message').value)); });

WebSockets - Some interesting projects

So before we jump into the security problems of WebSockets let me show some use cases of WebSockets.

  • Comments / Message notification in the website

This is a common use case in several application where the user needs to be notified on new comments or messages. There are UI components such as the notifications API (albeit still in draft) or the user defined components. These can be updated from any JS ofcourse. The usual update sequence has been from AJAX calls. However with WebSockets, we can make that relatively simpler since the server would send any updates when needed and the number of pings needed are very low thereby reducing both the client and server footprint.

  • Common editing platforms

With the advent of HTML5, shared editing is becoming very popular. Tools from google docs, word on the web, open source projects like etherpad etc have made it a reality. Though I cannot attest to the technology behind these tools in particular, I can definitely say that WebSockets can make this possible in a native manner. Since WebSockets follows the rules of http, the authorization mechanisms and sessions you have in place as part of your existing application also carries forward.

  • Monitoring solutions

In many cases we all monitor our servers, loads etc. and we use a http front end to see such activities in so-called real-time. The truth is that this is not as real-time as we presume since the cost to establish a secure tunnel is significant. WebSockets significantly reduce such latency for any real-time monitoring.

WebSockets - Security Overview

I know all the things I have said above make it difficult to justify that the current WebSocket specification itself poses some serious risks.

There have been some well known problems on the server side of the WebSockets and the most common solution has been to check the origin tag CSWSH. The discussions on this topic have even expanded to include the forgery of such socket communication despite the existence of the origin tag by re-routing the socket channel from the server instead of relying on the browser. While these are very valid problems my views on the subject is that we are not looking at the elephant in the room.

In the pursuit of securing the server, we often forget the end-user of the web-browser. Let us look at why WebSockets are so special that they represents a breaking point in HTML5.

Over so many years, we have been pushing for extended features in HTML5 that we have seemingly forgotten some of the design choices we made earlier. HTML5 does give us unlimited opportunities that the mere size of one article would never be able to cover. So I will restrain myself and talk only about a few things I would like to mention.

The first and foremost of them all is the beauty of CORS. CORS is a beauty in itself since it allows us to use a proper XMLHttpRequest across origins and achieve our purpose. Ok, let me go back to layman terms. In the browser, one of the major blocks of security has been the Same-Origin Policy. For those who understand SOP, please skip ahead to the next paragraph. The same-origin policy prevents a request made from one page to another if their origin is different. i.e. lets say http://yourwebsite.com wants to ask something from http://mywebsite.com, the browser would block it. This block meant that developers went around and over to find all the loopholes to overcome this. The browser allowed some resources to be shared (including scripts, images, video objects and such) and the developers used this mercilessly. The most used of these mechanisms is the use of JSONP.

I will write a separate article on JSONP but know that it uses the script tag's source attribute to make GET requests to cross-domain servers. Over the years, developers have been educated on the ill effects of JSONP and encouraged to add specific headers so as to be careful. Today one can bypass the use of JSONP completely by using CORS. The beauty of CORS allows one to send all kinds of request (note: JSONP allows only GET) to other domains.

Now we are almost getting off topic. So let me get back to WebSockets. I needed to provide CORS as an example because it matters significantly here. We have made a XMLHttpRequest so neat and well-balanced with proper support of a whitelist. In security, we always argue that a whitelist is better than a blacklist for obvious reasons. WebSockets is a nightmare because it does not come under the Same-origin policy.

WebSockets is a nightmare because it does not come under the Same-origin policy.

Why is this a nightmare? In case of WebSockets, we are left with very little choices. Once a much more unrestricted communication channel is established with the remote malicious server, there is no limitation on what can be done. The malicious server can then take its sweet time in evaluating every user action with no suspicious events making appropriate corrections and handling it accordingly. Further, if the channel established were a secure one, the limits of monitoring such a communication would increase as well. So it merely takes one malicious script to get so much access to one's site. This presents the single most damaging problem for any developer. Imagine, with all the modern events possible, one can monitor every DOM node for child changes, change any native action and goes without saying hijack the user's session and so on. It is almost impossible to disable this technology for your webpage since that is how JavaScript works.

Imagine http://yourwebsite.com loads information from many websites. This may be temperature information or an avatar (such as gravatar - though I am not accusing gravatar specifically of any wrongdoing) or feeds (such as twitter feeds - again not accusing twitter specifically). You tend to trust and load these scripts. Similarly, the use of content-delivery network (CDN) makes our applications vulnerable if the CDN was breached. Scripts could also be loaded into the website using the cross-site scripting vulnerability. The attacker/affected vendor at this time, need not worry about what information is important or how exactly to exploit the document object model (DOM). The script merely needs to establish a connection and from that point, the http://maliciouswebsite.com can take its own sweet time to do whatever it wants. I might have given access to you to use details from http://mywebsite.com after checking the origin diligently at my server and also get affected in the process by sending data that will routed to http://maliciouswebsite.com. Of course, these could have happened even without WebSockets but WebSockets reduces the complexity to do this. This allows unlimited possibilities and the attacker can keep using the client in so many ways by posting one payload after another.

I did mention earlier that JavaScript was getting more powerful and browsers were relaxing their limitations due to the presence of a sandbox. Imagine the power the hacker would get if a chrome app or firefox extension (which have access to even more powerful functionality) were to be affected. Note that WebSockets itself is not a problem and it is highly appreciated. The only non-logical problem seems to be the lack of enforcement of the same-origin policy.

It is as if we spent time to develop the perfect weapon and gave it to everyone to use at their discretion taking out the only armor we had.

Why we could not enforce the Same-origin policy on WebSockets when CORS is strictly made to follow the rules, I would never understand. It is like having normal border crossing and a teleporter. Every one using normal border crossing is checked thoroughly and anyone using the teleporter can feel free to come from any country. Then you make the teleporter available to every country. Grand idea ! No that is not enough. Make the teleporter so perfect that no one can disable it. We are truly geniuses !

This vulnerability can hence be called Cross-Site WebSockets Scripting since this is a variation of XSS that is made more potent by WebSockets.

WebSockets - Help me from this nightmare please

For now there is just a limited number of things a developer may do. The first and foremost of them all is to use your Content-Security-Policy header. Change it to reflect the following.

Content-Security-Policy : connect-src 'self'

The above prevents webSockets requests from any place but the current server. It also prevents CORS and EventSource requests. It is painful that this is only way available currently to disable this functionality. If you need to add other resources to CSP connect-src for CORS to be successful (note CORS needs one more header from the server side called Access-Control-Allow-Origin). If you need to add CORS support for a website, you will automatically authorize websocket requests as well. So be wary of it.

Even if you just use a pure html website, add this using the meta tag as follows and you should be good to go.

<meta http-equiv="Content-Security-Policy" content="connect-src 'self'">

For more details on content-security-policy, please check this out.

@jota12x
Copy link

jota12x commented Feb 7, 2020

Amazing article. Thanks !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment