





















































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:
(For more resources related to this topic, see here.)
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.
To lock down the HTTP referrer and only allow events to whitelisted domains, follow these steps:
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);
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;
}
});
socket.on('permission.message', function (data) {
document.querySelector('h1').innerHTML = data;
});
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.
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.
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.
To use the secure wss:// protocol, follow these steps:
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'
});
});
});
var socket = io('//localhost:5000', {
secure: true
});
socket.on('hello.client', function (data) {
console.log(data);
});
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.
In this article, we gave you an overview of the topics related to security in Socket.IO applications.
Further resources on this subject: