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.
Related
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
I am using socket io on client:
const socket = require('socket.io-client')('https://localhost:4200', {secure: true, rejectUnauthorized: false})
And on server:
let https = require('https')
let fs = require('fs')
let options = {
key: fs.readFileSync('cert/my.net.key'),
cert: fs.readFileSync('cert/my.net.cert'),
requestCert: false,
rejectUnauthorized: false,
};
const server = https.createServer(options, require('express')())
const io = require('socket.io')(server)
All services are started normally, but on client I am getting polling-xhr.js:263 GET https://localhost:4200/socket.io/?EIO=3&transport=polling&t=MPa6ZuL net::ERR_CERT_AUTHORITY_INVALID
Why? Whats is wrong?
Browsers don't like self-signed certificates for security reasons.
To get around this in your development environment, I see three options:
Use a certificate issued by a certification unit.
It could be something free, like https://letsencrypt.org/.
Create your server dynamically, based on the development environment, not to include certificates and work directly with HTTP and WS (and not HTTPS and WSS).
Change the configuration of your browser used in development so that it accepts self-signed certificates.
For Chrome, for example, just enable the Allow invalid certificates for resources loaded from localhost. (chrome://flags/#allow-insecure-localhost) setting.
But remember that you will not be able to use self-signed certificates in production environments.
I have a certificate bought from godaddy and it needs to be installed in our nodejs server.I have the below code written. This was working earlier but we recently renewed the certificate and replaced the old certificates with the renewed certificate and certificate private key file in the specified folder. But the certificate information is not getting reflected in the website. The expiry date for old certificate was june-20,2018.Will it not reflect till the old one gets expired?
var options = {
key: fs.readFileSync('certificate/mssp.tcs.com.key'),
cert: fs.readFileSync('certificate/mssp.tcs.com.crt'),
ca: [fs.readFileSync('certificate/mssp.tcs.comi.crt')]
};
var server = app.listen(8001, function () {
console.log('%s listening at %s', server.name, server.url);
console.log("server Started");
});
https.createServer(options,app).listen('443 ');
The way you are importing the certificate and key is correct. So I think your issue is probably caused by pointing to the old files or existing process using old certificates. In this situation I suggest:
Double check certificate/mssp.tcs.com.* files to ensure all of them are really pointing to the renewed files. Sometimes we have symbolic links to restricted folders and a simple copy might fail without correct privileges.
Once you ensure that new files are in place. Make sure you kill the older process that might be running. You can achieve that by a ps -ef | grep node on linux servers and then kill it by PID. Just be careful to stop the correct application. Once you stop the old process, you should get a 'Not Found' if you access your web application through a browser.
Ensuring you have new files in place and that old process is not running anymore, start your node process. Your certificates should be updated :)
Finally, I would also suggest you to review the server which is running on port 8001 without https. Just make sure you need that or remove it. A simple code to run HTTPS can be like that:
'use strict';
var express = require('express'),
fs = require('fs'),
https = require('https');
var httpPort = 443;
var app = express();
var options = {
key: fs.readFileSync('privkey.pem'),
cert: fs.readFileSync('cert.pem')
};
app.set('port', httpPort);
app.use(express.static(path.join(__dirname, '/public')));
https.createServer(options, app).listen(httpPort, function(){
console.log('Listening at ' + httpPort);
});
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.
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.