node.js mqtt client using TLS - node.js

I am trying to implement a node.js mqtt client with TLS using the package below;
https://www.npmjs.com/package/mqtt#client
The code for running mqtt client without TLS is as follows;
var mqtt = require('mqtt')
var client = mqtt.connect('mqtt://test.mosquitto.org')
client.on('connect', function () {
client.subscribe('presence')
client.publish('presence', 'Hello mqtt')
})
client.on('message', function (topic, message) {
// message is Buffer
console.log(message.toString())
client.end()
})
How should the above code be modified to use TLS on the mqtt client?
The mosca MQTT broker was run as a stand-alone using the command below;
mosca --key ./tls-key.pem --cert ./tls-cert.pem --http-port 3000 --http-bundle --http-static ./ | pino

Should be enough to change the protocol part of the URL to mqtts://
mqtts://test.mosquitto.org.
Self-signed certificates
You can pass the following option to the connect function when using self-signed certificates (for testing purposes only):
mqtt.connect('mqtts://test.mosquitto.org', {
rejectUnauthorized: false
});

You need to provide the mqtt.connect() function with an options object which includes the CA certificate to use to verify the connection.
The options object needs to include a ca key that points to the certificate used to sign the brokers certificate. As it looks like your using a self signed certificate this will be the same one used by the broker.
The ca key is described here
Or you can allow any certificate with the rejectUnauthorized key as mentioned in #notion's answer. But that makes it impossible to detect if somebody is impersonating your broker

Related

Node.js Which is the better way to implement server ssl certificate

I see that there are two ways to add server ssl certificate with Node.js
First:
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
ca: [ fs.readFileSync('client-cert.pem') ]
};
var server = tls.createServer(option);
server.listen(8000, function() {
console.log('server bound');
});
Second:
pfx: fs.readFileSync('server.pfx')
};
var server = tls.createServer(options);
server.listen(8000, function() {
console.log('server bound');
});
Currently i am using the second option, but I am asked to change it first. I want to understand what are the advantages/disadvantages by changing it to the first option.
None is better. The Pfx file is a PKCS#12 archive, it contains the server's certificate and private key as well as some certificate authorities (used to decide if a client certificate is valid). Pfx files are password protected, but since Node.js has to be able to read the file, you have to provide the password, so it's not really more secure.
I would say that the best option is not to use TLS in Node.js at all because it is quite slow and to put your application behind an SSL proxy (Nginx for example) which is much more efficient.

Added SSL to client side server but still not performing handshake with backend server nodejs

I am trying to implement SSL on my nodejs project. Currently, my servers are split between a client side server running on localhost port 443 and a backend server running on localhost port 5000. I have already added a self-signed SSL certificate by openSSL to my client side server as shown below.
Now here's my issue. When I send a post request to login, from what I understand, a handshake is suppose to happen between the server and the client to make a secure connection. However, that's not the case. When I used Wireshark the intercept the packets, there is no handshake happening in the process.
I am currently not sure on how to proceed because I have limited knowledge on this kind of security topics. Do I need to sign a new key and cert and add it to my backend server? Or am I doing everything wrong? If so, can I get a source or guide on how to properly create one for a nodejs server?
you have many options here for securing your backend server :
first, you can use Nginx reverse proxy server and you can add ssl/tls logic to it. nginx will handle this stuff for you.
second, you can use [https][1] package directly and pass your SSL certificate and key to it :
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};
https.createServer(options, (req, res) => {
res.writeHead(200);
res.end('hello world\n');
}).listen(8000);
remember that the domain name your are trying to access must be set in your host ip.
[1]: https://nodejs.org/api/https.html

Securing Node Redis

I'm trying to secure the Node Redis IPC server to use a private/public key. I've followed this tutorial which uses stunnel which wraps the tunnel used by Redis under a SSL layer.
The example is not for Node, but it does secure the connection, and I only can connect to the server if I include the certification in my config file, otherwise the connection is reseted.
However, I cannot replicate this with NodeJS. On my server computer, I have:
var redis = require('redis');
var client = redis.createClient();
client.auth('myPassword');
client.publish('instances', 'start');
And my on my client computer I have:
var redis = require('redis');
var client = redis.createClient();
client.auth('myPassword');
client.subscribe('instances');
client.on('message', function (channel, message) {
console.log("Got message " + message + " from channel " + channel);
})
But, the two devices communicate whether or not I include the certification in my stunnel config file. How can I secure this connection up?
Cheers
You can do this by passing in the tls configuration when creating the client like so
var redis = require("redis");
var client = redis.createClient(6380,'location.of.server', {auth_pass: 'password', tls: {servername: 'location.of.server'}});
I have also searched for this . But redis doesn't need any ssl since in runs only on verified private networks. The only way to provide security using stunnel. Since we can enable password by using AUTH command. In redis they have provided a password generator which is named as GPG key. Which generated a 2048 length key which will provide security. I think my answer is relevant.

Using Node.js MQTT with Mosquitto PSK Encryption

I'm working on developing a solution using MQTT to send/receive data to embedded systems. For a broker I'm using Mosquitto. For the client I'm using Node.js MQTT.
I need to encrypt the data and I'd like to use the pre-shared key option in mosquitto to accomplish this however, I can't seem to find anything built into the Node.js MQTT package to do this. Is this possible?
From the Mosquitto configuration docs:
When using pre-shared-key based encryption through the psk_hint and
psk_file options, the client must provide a valid identity and key in
order to connect to the broker before any MQTT communication takes
place. If use_identity_as_username is true, the PSK identity is used
instead of the MQTT username for access control purposes. If
use_identity_as_username is false, the client may still authenticate
using the MQTT username/password if using the password_file option.
Node does support TLS-PSK now, but PSK ciphers are disabled by default.
I finally could connect with the following options:
const client = mqtt.connect('mqtts://localhost:8883', {
pskCallback: (hint) => {
console.log('psk_hint configured in mosquitto.conf', hint);
return {
psk: Buffer.from('1234', 'hex'),
identity: 'DeviceId',
};
},
ciphers: crypto.constants.defaultCipherList.replace(':!PSK', ''),
});
psk_file must include the line DeviceId:1234 in this example.
My main problem was, that configuring a custom ciphers list must include HIGH for whatever reason. It even works with ciphers: 'HIGH'
It appears the MQTT package hands off to Node's TLS capabilities and Node doesn't support TLS PSK.
Preshared keys (TLS-PSK-WITH-AES-256-CBC-SHA) with node.js server

How to verify that a connection is actually TLS secured?

I have created a TLS server and an appropriate TLS client in Node.js. Obviously they both work with each other, but I would like to verify it.
Basically, I think of something such as inspecting the connection, or manually connecting to the server and inspecting what it sends, or something like that ...
The relevant code of the server is:
var tlsOptions = {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('server.pem')
};
tls.createServer(tlsOptions, function (tlsConnection) {
var d = dnode({
// [...]
});
tlsConnection.pipe(d).pipe(tlsConnection);
}).listen(3000);
The appropriate client code is:
var d = dnode();
d.on('remote', function (remote) {
// [...]
});
var tlsConnection = tls.connect({
host: '192.168.178.31',
port: 3000
});
tlsConnection.pipe(d).pipe(tlsConnection);
How could I do that?
Wireshark will tell you if the data is TLS encrypted, but it will not tell you if the connection is actually secure against Man-in-the-Middle attacks. For this, you need to test if your client refuses to connect to a server that provides a certificate not signed by a trusted CA, a certificate only valid for a different host name, a certificate not valid anymore, a revoked certificate, ...
If your server.pem is not a certificate from a real/trusted CA, and your client doesn't refuse to connect to the server (and you didn't explicitly provide server.pem to the client), then your client is very probably insecure. Given that you are connecting to an IP, not a host name, no trusted CA should have issued a certificate for it, so I assume you use a selfsigned one and are vulnerable. You probably need to specify rejectUnauthorized when connect()ing. (Rant: As this is a pretty common mistake, I think it is extremely irresponsible to make no verification the default.)
Basically, I think of something such as inspecting the connection, or manually connecting to the server and inspecting what it sends, or something like that ...
You can use tools such as Wireshark to see the data they are transmitting.

Resources