6 min read

In this article by Tyson Cadenhead, author of Socket.IO Cookbook, we will explore several topics related to security in Socket.IO applications. These topics will cover the gambit, from authentication and validation to how to use the wss:// protocol for secure WebSockets. As the WebSocket protocol opens innumerable opportunities to communicate more directly between the client and the server, people often wonder if Socket.IO is actually as secure as something such as the HTTP protocol. The answer to this question is that it depends entirely on how you implement it. WebSockets can easily be locked down to prevent malicious or accidental security holes, but as with any API interface, your security is only as tight as your weakest link.

In this article, we will cover the following topics:

  • Locking down the HTTP referrer
  • Using secure WebSockets

(For more resources related to this topic, see here.)

Locking down the HTTP referrer

Socket.IO is really good at getting around cross-domain issues. You can easily include the Socket.IO script from a different domain on your page, and it will just work as you may expect it to.

There are some instances where you may not want your Socket.IO events to be available on every other domain. Not to worry! We can easily whitelist only the http referrers that we want so that some domains will be allowed to connect and other domains won’t.

How To Do It…

To lock down the HTTP referrer and only allow events to whitelisted domains, follow these steps:

  1. Create two different servers that can connect to our Socket.IO instance. We will let one server listen on port 5000 and the second server listen on port 5001:
    var express = require('express'),
    app = express(),
    http = require('http'),
    socketIO = require('socket.io'),
    server, server2, io;
    
    app.get('/', function (req, res) {
    res.sendFile(__dirname + '/index.html');
    });
    
    server = http.Server(app);
    server.listen(5000);
    
    server2 = http.Server(app);
    server2.listen(5001);
    
    io = socketIO(server);
  2. When the connection is established, check the referrer in the headers. If it is a referrer that we want to give access to, we can let our connection perform its tasks and build up events as normal. If a blacklisted referrer, such as the one on port 5001 that we created, attempts a connection, we can politely decline and perhaps throw an error message back to the client, as shown in the following code:
    io.on('connection', function (socket) {
    switch (socket.request.headers.referer) {
    case 'http://localhost:5000/':
    socket.emit('permission.message', 'Okay, you're cool.');
    break;
    default:
    returnsocket.emit('permission.message', 'Who invited you to this party?');
    break;
    }
    });
  3. On the client side, we can listen to the response from the server and react as appropriate using the following code:
    socket.on('permission.message', function (data) {
    document.querySelector('h1').innerHTML = data;
    });

How It Works…

The referrer is always available in the socket.request.headers object of every socket, so we will be able to inspect it there to check whether it was a trusted source.

In our case, we will use a switch statement to whitelist our domain on port 5000, but we could really use any mechanism at our disposal to perform the task. For example, if we need to dynamically whitelist domains, we can store a list of them in our database and search for it when the connection is established.

Using secure WebSockets

WebSocket communications can either take place over the ws:// protocol or the wss:// protocol. In similar terms, they can be thought of as the HTTP and HTTPS protocols in the sense that one is secure and one isn’t. Secure WebSockets are encrypted by the transport layer, so they are safer to use when you handle sensitive data.

In this recipe, you will learn how to force our Socket.IO communications to happen over the wss:// protocol for an extra layer of encryption.

Getting Ready…

In this recipe, we will need to create a self-signing certificate so that we can serve our app locally over the HTTPS protocol. For this, we will need an npm package called Pem. This allows you to create a self-signed certificate that you can provide to your server. Of course, in a real production environment, we would want a true SSL certificate instead of a self-signed one. To install Pem, simply call npm install pem –save.

As our certificate is self-signed, you will probably see something similar to the following screenshot when you navigate to your secure server:

Just take a chance by clicking on the Proceed to localhost link. You’ll see your application load using the HTTPS protocol.

How To Do It…

To use the secure wss:// protocol, follow these steps:

  1. First, create a secure server using the built-in node HTTPS package. We can create a self-signed certificate with the pem package so that we can serve our application over HTTPS instead of HTTP, as shown in the following code:
    var https = require('https'),
    pem = require('pem'),
    express = require('express'),
    app = express(),
    socketIO = require('socket.io');
    
    // Create a self-signed certificate with pem
    pem.createCertificate({
    days: 1,
    selfSigned: true
    }, function (err, keys) {
    
    app.get('/', function(req, res){
    res.sendFile(__dirname + '/index.html');
    });
    
    // Create an https server with the certificate and key from pem
    var server = https.createServer({
    key: keys.serviceKey,
    cert: keys.certificate
    }, app).listen(5000);
    
    vario = socketIO(server);
    
    io.on('connection', function (socket) {
    var protocol = 'ws://';
    
         // Check the handshake to determine if it was secure or not
    if (socket.handshake.secure) {
    protocol = 'wss://';
         }
    
    socket.emit('hello.client', {
    message: 'This is a message from the server. It was sent using the ' + protocol + ' protocol'
         });
    });
    });
  2. In your client-side JavaScript, specify secure: true when you initialize your WebSocket as follows:
    var socket = io('//localhost:5000', {
    secure: true
    });
    
    socket.on('hello.client', function (data) {
    console.log(data);
    });
  3. Now, start your server and navigate to https://localhost:5000. Proceed to this page. You should see a message in your browser developer tools that shows, This is a message from the server. It was sent using the wss:// protocol.

How It Works…

The protocol of our WebSocket is actually set automatically based on the protocol of the page that it sits on. This means that a page that is served over the HTTP protocol will send the WebSocket communications over ws:// by default, and a page that is served by HTTPS will default to using the wss:// protocol.

However, by setting the secure option to true, we told the WebSocket to always serve through wss:// no matter what.

Summary

In this article, we gave you an overview of the topics related to security in Socket.IO applications.

Resources for Article:


Further resources on this subject:


LEAVE A REPLY

Please enter your comment!
Please enter your name here