node.js, socket.io with SSL

225,048

Solution 1

Use a secure URL for your initial connection, i.e. instead of "http://" use "https://". If the WebSocket transport is chosen, then Socket.IO should automatically use "wss://" (SSL) for the WebSocket connection too.

Update:

You can also try creating the connection using the 'secure' option:

var socket = io.connect('https://localhost', {secure: true});

Solution 2

The following is how I set up to set it up with express:

    var app = require('express')();
    var https = require('https');
    var fs = require( 'fs' );
    var io = require('socket.io')(server);

    var options = {
        key: fs.readFileSync('./test_key.key'),
        cert: fs.readFileSync('./test_cert.crt'),
        ca: fs.readFileSync('./test_ca.crt'),

        requestCert: false,
        rejectUnauthorized: false
    }

    var server = https.createServer(options, app);
    server.listen(8080);
    

    
    io.sockets.on('connection', function (socket) {
        // code goes here...
    });
    
    app.get("/", function(request, response){
        // code goes here...
    })
   

Update : for those using lets encrypt use this

var server = https.createServer({ 
                key: fs.readFileSync('privkey.pem'),
                cert: fs.readFileSync('fullchain.pem') 
             }, app);

Solution 3

On the same note, if your server supports both http and https you can connect using:

var socket = io.connect('//localhost');

to auto detect the browser scheme and connect using http/https accordingly. when in https, the transport will be secured by default, as connecting using

var socket = io.connect('https://localhost');

will use secure web sockets - wss:// (the {secure: true} is redundant).

for more information on how to serve both http and https easily using the same node server check out this answer.

Solution 4

If your server certificated file is not trusted, (for example, you may generate the keystore by yourself with keytool command in java), you should add the extra option rejectUnauthorized

var socket = io.connect('https://localhost', {rejectUnauthorized: false});

Solution 5

Depending on your needs, you could allow both secure and unsecure connections and still only use one Socket.io instance.

You simply have to instanciate two servers, one for HTTP and one for HTTPS, then attach those servers to the Socket.io instance.

Server side :

// needed to read certificates from disk
const fs          = require( "fs"    );

// Servers with and without SSL
const http        = require( "http"  )
const https       = require( "https" );
const httpPort    = 3333;
const httpsPort   = 3334;
const httpServer  = http.createServer();
const httpsServer = https.createServer({
    "key" : fs.readFileSync( "yourcert.key" ),
    "cert": fs.readFileSync( "yourcert.crt" ),
    "ca"  : fs.readFileSync( "yourca.crt"   )
});
httpServer.listen( httpPort, function() {
    console.log(  `Listening HTTP on ${httpPort}` );
});
httpsServer.listen( httpsPort, function() {
    console.log(  `Listening HTTPS on ${httpsPort}` );
});

// Socket.io
const ioServer = require( "socket.io" );
const io       = new ioServer();
io.attach( httpServer  );
io.attach( httpsServer );

io.on( "connection", function( socket ) {

    console.log( "user connected" );
    // ... your code

});

Client side :

var url    = "//example.com:" + ( window.location.protocol == "https:" ? "3334" : "3333" );
var socket = io( url, {
    // set to false only if you use self-signed certificate !
    "rejectUnauthorized": true
});
socket.on( "connect", function( e ) {
    console.log( "connect", e );
});

If your NodeJS server is different from your Web server, you will maybe need to set some CORS headers. So in the server side, replace:

const httpServer  = http.createServer();
const httpsServer = https.createServer({
    "key" : fs.readFileSync( "yourcert.key" ),
    "cert": fs.readFileSync( "yourcert.crt" ),
    "ca"  : fs.readFileSync( "yourca.crt"   )
});

With:

const CORS_fn = (req, res) => {
    res.setHeader( "Access-Control-Allow-Origin"     , "*"    );
    res.setHeader( "Access-Control-Allow-Credentials", "true" );
    res.setHeader( "Access-Control-Allow-Methods"    , "*"    );
    res.setHeader( "Access-Control-Allow-Headers"    , "*"    );
    if ( req.method === "OPTIONS" ) {
        res.writeHead(200);
        res.end();
        return;
    }
};
const httpServer  = http.createServer( CORS_fn );
const httpsServer = https.createServer({
        "key" : fs.readFileSync( "yourcert.key" ),
        "cert": fs.readFileSync( "yourcert.crt" ),
        "ca"  : fs.readFileSync( "yourca.crt"   )
}, CORS_fn );

And of course add/remove headers and set the values of the headers according to your needs.

Share:
225,048

Related videos on Youtube

Beyond
Author by

Beyond

Updated on September 17, 2021

Comments

  • Beyond
    Beyond over 2 years

    I'm trying to get socket.io running with my SSL certificate however, it will not connect.

    I based my code off the chat example:

    var https = require('https');
    var fs = require('fs');
    /**
     * Bootstrap app.
     */
    var sys = require('sys')
    require.paths.unshift(__dirname + '/../../lib/');
    
    /**
    * Module dependencies.
    */
    
    var express = require('express')
      , stylus = require('stylus')
      , nib = require('nib')
      , sio = require('socket.io');
    
    /**
     * App.
     */
    var privateKey = fs.readFileSync('../key').toString();
    var certificate = fs.readFileSync('../crt').toString();
    var ca = fs.readFileSync('../intermediate.crt').toString();
    
    var app = express.createServer({key:privateKey,cert:certificate,ca:ca });
    
    
    /**
     * App configuration.
     */
    
    ...
    
    /**
     * App routes.
     */
    
    app.get('/', function (req, res) {
      res.render('index', { layout: false });
    });
    
    /**
     * App listen.
     */
    
    app.listen(443, function () {
      var addr = app.address();
      console.log('   app listening on http://' + addr.address + ':' + addr.port);
    });
    
    /**
     * Socket.IO server (single process only)
     */
    
    var io = sio.listen(app,{key:privateKey,cert:certificate,ca:ca});
    ...
    

    If I remove the SSL code it runs fine, however with it I get a request to http://domain.com/socket.io/1/?t=1309967919512

    Note it's not trying https, which causes it to fail.

    I'm testing on chrome, since it is the target browser for this application.

    I apologize if this is a simple question, I'm a node/socket.io newbie.

    Thanks!

    • kanaka
      kanaka almost 13 years
      Is your client trying to connect to a 'wss://' prefixed URI.
    • Beyond
      Beyond almost 13 years
      nope it doesnt get there, it makes the request to domain.com/socket.io/1/?t=1309967919512 then dies.
    • kanaka
      kanaka almost 13 years
      How are you specifying the address to connect to? "domain.com" sounds like a placeholder in the socket.io client-side library. Can you post your client Javascript code that you are using to connect?
    • Beyond
      Beyond almost 13 years
      the project is on github: github.com/BCCasino/BCCasino
    • Beyond
      Beyond almost 13 years
      basically becasue its node.js socket.io magically handles the client side stuff, all you do is run socket.connect
    • Beyond
      Beyond almost 13 years
      in github the code in question is test2.js and index.jade (backend and frontend respetively)
    • Samuel Robert
      Samuel Robert about 8 years
      I was trying to make a connection using wss uri it gives me URISyntaxException. Any idea why?
  • Beyond
    Beyond almost 13 years
    we do this. we goto https : // www.thebitcoinwheel.com and it still makes a request to http automatically, this is something with the socket.io code and is the point of the question.
  • XiaoChuan Yu
    XiaoChuan Yu almost 11 years
    {secure: true} should not be needed if you specify 'https' in the url. Here is an excerpt from socket.io client source secure: 'https' == uri.protocol (version 0.9.16), it sets secure option to true if https is detected in the url.
  • Chiara Coetzee
    Chiara Coetzee over 10 years
    I tried this with an https URL and indeed {secure: true} was not required to function correctly.
  • gabeio
    gabeio over 10 years
    I believe it would be prudent to assure that the connection is secure by using both secure:true and issuing an https url to the client side. This way no matter what you know it will be a secure connection.
  • Francisco Hodge
    Francisco Hodge over 7 years
    This is the only solution that worked for me. Thanks for saving my time.
  • RozzA
    RozzA over 7 years
    worked good for me after a bit of trial and error with the certs
  • Hugo Rune
    Hugo Rune over 6 years
    This solution worked perfect for me, thanks. If you're using the free certs from letsencrypt.org then you can use the following code.. var server = https.createServer({ key: fs.readFileSync('/etc/letsencrypt/live/domain.name/privkey.p‌​em'), cert: fs.readFileSync('/etc/letsencrypt/live/domain.name/cert.pem'‌​), ca: fs.readFileSync('/etc/letsencrypt/live/domain.name/chain.pem‌​'), requestCert: false, rejectUnauthorized: false },app); server.listen(3000);
  • Harsha Jasti
    Harsha Jasti almost 6 years
    Thanks a lot for this answer. It has been of tremendous help to me.
  • bvdb
    bvdb about 5 years
    would be appreciated if you added an example explaining how you used the keytool to create that key for node. 'Cause keys are so complicated and there just aren't enough tutorials about it.
  • clevertension
    clevertension about 5 years
    keytool is a tool inside the Java Development Kit (JDK). you can referer this docs.oracle.com/javase/10/tools/…
  • Pablo Pazos
    Pablo Pazos over 4 years
    I cracked my head looking for a solution in SocketIO and the issue was to config express, fantastic...
  • Martin Schneider
    Martin Schneider over 4 years
    "rejectUnauthorized: false WARNING: it leaves you vulnerable to MITM attacks!"
  • Eric
    Eric over 4 years
    Thanks, worked like a charm with letsencrypt and .pem files