Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJS Server-Server Secure Communication

I have two servers using NodeJS on the same domain. (server1.example.com and server2.example.com)

I'd like to send messages from one server to the other in a secure way.

Right now, I'm using sending my message by sending a HTTPS POST such as https://example.com with {secret:XXXXX,message:1234}.

Is there a cleaner way to do this? If so, what would be the exact steps needed? Note: I have a SSL certifiate on the site. Both servers are on the same domain.

like image 664
RainingChain Avatar asked Apr 03 '17 23:04

RainingChain


People also ask

Is node js server secure?

Node. js is one such technology that developers use for web application development. It is designed to be completely secure.

How do I make a Node server https?

To built an HTTPS server with nodeJs, we need an SSL (Secure Sockets Layer) certificate. We can create a self-signed SSL certificate on our local machine. Let's first create an SSL certificate on our machine first. After running this command, we would get some options to fill.


2 Answers

There are a few options I can think of, though of course, it depends on the amount of encryption and security you are looking for, if HTTPS is not strong enough for what is required for the specific communication. (Though as mentioned you do have HTTPS.)

  1. You can have the sending server issue a whole JWT route. Send the information with a Token and verify it on the other side. Make sure the Token has a short TTL as well. (If you really want to go for broke here, you can start implementing the OAuth2 framework, though that may be complete overkill.)

  2. Additionally, you can create a Websocket HTTPS server, and only accept the information from the information on a specific incoming port. That will allow you to use the JWT and further verify by the port access. The port you open will only be allowed to accept packets from a specific IP, which is your outgoing server.

  3. You can add yet another layer as you are doing by encrypting the entire message (via one of the Node NPM modules or via Crypto), so both the message and secret are hashed.

  4. You can also add a cache layer (either Redis or a Node-Cache module), where all the decryption will be done to speed up the process.

  5. Another trick to use, though you have to work out the actual schedule, is to mix up the various hashing routines you use based on process or on different hours, or whatever schedule you wish.

  6. Finally, a sometimes overlooked option is to install a firewall on the receiving computer with very specific rules on what to receive and from where. (This though is not a Node process, and can take time to get right.)

None of the above is linked to Express though, or middleware. I assume if you adopt any of the above you will need a few NPM modules in the end result.

Probably forgot a few options but hope this helps.

like image 68
twg Avatar answered Sep 29 '22 20:09

twg


Just to add to the other solutions already posted, you could just use certificates on both ends, that way you could do the authentication at the TLS layer instead of the application layer if that helps at all.

Assuming you're using node on both servers, you could achieve this like so:

  1. Create one custom CA, and then one certificate and one private key for each server.
  2. Each server might have code like:

    const tls = require('tls');
    
    function onMessage(socket, msg) {
      // `msg` is a parsed message received from `socket`
    }
    
    // Receive incoming messages
    tls.createServer({
      rejectUnauthorized: true,
      requestCert: true,
      ca: MY_CUSTOM_CA,
      cert: THIS_SERVER_CERT,
      key: THIS_SERVER_PRIVATE_KEY
    }, (socket) => {
      if (!socket.authorized)
        return socket.end(); // Certificate did not check out
      console.log('Connection accepted');
    
      // example protocol: newline-delimited JSON
      var jsonBuffer = '';
      socket.setEncoding('utf8');
      socket.on('data', (chunk) => {
        var chunks = chunk.split('\n');
        var numComplete = chunks.length - 1;
        // Last line does not have trailing newline
        var incompleteChunk = chunks[numComplete];
        if (numComplete === 0) {
          jsonBuffer += incompleteChunk;
          return;
        }
        chunks[0] = jsonBuffer + chunks[0];
        for (var i = 0; i < numComplete; ++i) {
          try {
            onMessage(socket, JSON.parse(chunks[i]));
          } catch (ex) {}
        }
        jsonBuffer = incompleteChunk;
      });
    
      socket.on('end', () => {
        console.log('Connection ended');
      });
    }).listen(MY_PORT);
    
    // Send outgoing messages
    function sendMessages(host, port, msgs, cb) {
      if (!Array.isArray(msgs))
        msgs = [msgs];
    
      var req = tls.connect({
        host,
        port,
        rejectUnauthorized: true,
        ca: MY_CUSTOM_CA,
        cert: THIS_SERVER_CERT,
        key: THIS_SERVER_PRIVATE_KEY
      }, () => {
        if (!this.authorized)
          return this.end(); // Certificate did not check out
    
        for (var i = 0; i < msgs.length; ++i)
          this.write(JSON.stringify(msgs[i]) + '\n');
    
        this.end();
      }).once('error', onError).once('close', onClose);
    
      function onError(err) {
        req.removeListener('close', onClose);
        cb(err);
      }
    
      function onClose() {
        cb();
      }
    }
    
  3. Add incoming message handling in onMessage() and send outgoing messages with sendMessages().

You could also just keep a single socket open all the time instead of using a new connection per set of outgoing messages, but that would be a little more involved because you'd need to add an application-level keepalive mechanism and such, but it's certainly doable.

like image 37
mscdex Avatar answered Sep 29 '22 18:09

mscdex