TLS what exactly does 'rejectUnauthorized' mean for me? - node.js

So, I was having an issue earlier today where my client, written in node, was barfing because the server I was connecting to used self signed certs. So, I went and added the option rejectUnauthorized: false to my tls.connect command like any unwitting developer would do.
My question is now, what the hell does this mean for me? Is my TLS connection just a vanilla TCP connection that can also possibly be a TLS connection? Is writing this as a TLS stream totally useless?
More importantly, that server, you know the one with the self-signed certs? Is my stream between here and there actually encrypted?

As described in the documentation:
rejectUnauthorized: If true, the server certificate is verified against the list of supplied CAs. An error event is emitted if verification fails; err.code contains the OpenSSL error code. Default: true.
Since you're using self-signed certificates, obviously there won't be a match with the built-in CAs, so by default the connection would be rejected because it cannot verify the server is who they say they are.
By setting rejectUnauthorized: false, you're saying "I don't care if I can't verify the server's identity." Obviously this is not a good solution as it leaves you vulnerable to MITM attacks.
A better solution for self-signed certificates is to set the appropriate ca value to your custom CA when connecting client-side. Also, make sure your host value matches that of the Common Name of the server's self-signed certificate. For example:
var socket = tls.connect({
host: 'MyTLSServer',
port: 1337,
ca: [ fs.readFileSync('CA.pem') ],
}, function() {
// Connected!
});
// ...
No matter if you use rejectUnauthorized: false or set ca, the connection is encrypted.

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.

TLS connectivity in NodeJS using certificate and key from Certificate store

I have implemented TLS connectivity through MQTT as shown below.
mqttOptions = {
clientId: '100',
key: fs.readFileSync('test/certs/client.key'),
cert: fs.readFileSync('test/certs/client.crt'),
ca: fs.readFileSync('test/certs/ca.crt'),
secureProtocol: 'TLSv1_method',
rejectUnauthorized: false,
protocolId: 'MQIsdp',
protocolVersion: 3,
passphrase: 'edgenode',
keepAlive: 1000,
clean: false,
reconnectPeriod: '1000',
will: willMessage
};
var client = mqtt.connect(tls://localhost:8883, mqttOptions);
Here I'm passing in the client key, certificate and the CA certificate. Instead I need to connect using information from Windows certificate store. Assuming I have installed the client certificate in Windows Certificate Store, how can I read the private key from it using nodeJS and establish connectivity? Please advice.
Access to the Windows Keystore is via the MSCAPI.
I can't see any NodeJS wrappers for this API on npm but even if there were you would have to modify the mqtt library to work with it as it won't actually give you access to the private client keys, but instead you pass in data to be signed/encrypted using that key and it gives you back the signed/encrypted data like a hardware crypto device.
If you really need to use the Windows keystore I would suggest porting the client app to something like C, C# or Java as there are MSCAPI libraries for these that present the keys/certs using the language standard APIs

Using Node.js with TLS getting HTTP hostname wrong error

I'm trying to write a simple Node.js server applicationthat will accept client requests, and allowing me to change the TLS/SSL protocol to use. It works fine with a browser (Firefox).
However, when I call the Node.js server from WebSphere Liberty Profile, no matter which TLS/SSL protocol I try to use, I am getting the very confusing error message:
[ERROR ] IOException invoking https://dlwester:32080/W3CookieServiceEmulator/workplace/services/w3cookie/callback/auth_data: HTTPS hostname wrong: should be <dlwester>
As you can see, it's telling me I'm using the wrong hostname, but the hostname it's telling me I should be using is what I'm already using. I've even tried using port 443, so that I don't need to specify a port, but it still gives me the same error message.
I'm not sure if the error is with Node.js or my WLP code (using JAX-RS client). I've not found a way in Node.js to bypass verifying the hostname.
var options = {
key: 'my.key',
cert: 'my.cert',
ciphers: 'TLSv1.2,TLSv1.1,TLSv1.0,SSLv3',
honorCipherOrder: true,
rejectUnauthorized: false
}
server = https.createServer(options, requestListener);
So I guess that's my first question - can I bypass hostname verification?
Has anyone else run into this error, and know a way to get around it?
This is the client verifying the hostname, not the server. You never mentioned the hostname used in your certificate -- if it doesn't match the hostname you use to address it from the client: fix the certificate.

How can I know that a HTTPS endpoint receiving a TLS request from my node.js is using a specified SSL certificate?

I have an endpoint (in any language, let's say Python) that exposes some service as HTTPS using a certificate issued by any widely known and trusted CA, that is
probably included in virtually any browser in the world.
The easiest part is that I can issue TLS requests against this endpoint using Node.js with no further problems.
For security reasons, I would like to check that every time my Node.js issues a TLS request against this HTTPS endpoint, I want to make sure that the certificate being used, is the certificate that I trust, and the one that was requested by my company.
What is the best way to accomplish that?
It sounds like the answer at How to get SSL certificate information using node.js? would be suitable for your needs.
You can use the following code to get your endpoint's certificate then check its fingerprint or hash against what you expect.
var https = require('https');
var options = {
host: 'google.com',
port: 443,
method: 'GET'
};
var req = https.request(options, function(res) {
console.log(res.connection.getPeerCertificate());
});
req.end();

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