Skip to content

Instantly share code, notes, and snippets.

@VRMink
Last active January 6, 2020 20:31
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save VRMink/5169211 to your computer and use it in GitHub Desktop.
Save VRMink/5169211 to your computer and use it in GitHub Desktop.
How to setup a private node.js web server with SSL authorization

Secure private web server with NodeJS

This article will explain how to set up a secure web server with NodeJS which only accepts connection from users with SSL certificates that you have signed. This is an efficient way to ensure that no other people are able to access the web server, without building a login system which will be significantly weaker.

I will not explain how to create a certificate authority (CA), create certificates or sign them. If you need to read up on this, have a look at this excelent article on how to do it with OpenSSL (Mac and Linux): https://help.ubuntu.com/community/OpenSSL#Practical_OpenSSL_Usage It is also possible to do this on a Mac with the keychain application, and I assume it is possible on a Windows machine aswell.

This architecture will allow you to have one web server communicating with an array of trusted clients, the web server itself can be on the public internet, that will not decrease the level of security, but it will only server trusted clients so it might aswell be on an internal network.

It is important you generate the CA yourself. The CA will control which client are authoried to connect to your application

Certificates

  1. Create a Certificate Authority.
  2. Create a server certificate and sign it.
  3. Create a number of client certificates and sign all except one.

The client and server certificates can be created in the same way for the purpose of this article, just make sure you know which certificates are ment for which role.

NodeJS Code

var options = {
    requestCert: true, //This will request the client certificate
    rejectUnauthorized: true, //This will reject client certificates that are not signed by the CA
  key: fs.readFileSync(CONFIG.certificates.privateKey),
  cert: fs.readFileSync(CONFIG.certificates.publicKey),
  ca: fs.readFileSync(CONFIG.certificates.ca) //This is crucial to ensure only people with the same root CA can connect
}
var server = https.createServer(options, app);
// This bit is for testing
server.on('secureConnection', function (cleartextStream, encryptedStream) {
    if(!cleartextStream.authorized){
        console.error("TLS error: " + cleartextStream.authorizationError)
    }
})
// The testing bit ends here
server.listen(443)

The above code will listen on port 443 and verify all SSL connections agains the CA specified. Verified connections will go directly to your requestListener, which in this code is "app". See the nodeJS for an explaination of how requests are handled internally (http://nodejs.org/dist/v0.6.19/docs/api/https.html#https_https_createserver_options_requestlistener). If you use Express of Connect, you can use your application object. This code is used with Express JS.

Make sure that the certificate used by the server has the same hostname as the server it is running on. This can be localhost for testing. Replace the key, cert and ca in the options object with your own certificates file paths.

In the options object you set the private key, the public key (certificate) and the Certificate Authority. When rejectUnauthorized == true, then all unauthorized connections will be dropped before they reach you application, so in this case the listener on 'secureConnection' will never be called. If you wish to verify wheter a connection would be dropped, simply set rejectUnauthoried to false (default), and the listener will be called when a connection is about to be rejected.

Testing

For the purpose of testing the web server we will use curl to ensure that the web server accepts connections from certificates signed by our CA, but drops all other connections.

Curl will need to know the certificate of the common CA, else it will not trust the server. The localhost_client_crt.pem should be signed by the common CA, the roguehacker.pem should not be signed, as we will use it to ensure that unsigned certificates can not access the server

# CA signed certificate and key, should work
curl https://localhost:443/  --cacert /fullPathTo/cacert.crt --key /fullPathTo/localhost_client_key.pem --cert /fullPathTo/localhost_client_crt.pem  -vvv

# Rogue certificate and key, should NOT work
curl https://localhost:443/ --key /fullPathTo/roguehacker.pem --cacert /fullPathTo/cacert.pem -vvv

When you use the rogue certificate and rejectUnauthorized == false, you should see a TLS error being printed on the console, remeber to set rejectUnauthorized == true, to protect the web server.

Enjoy

This is all there is to it! :) Now you have a secure nodeJS web server.

Thanks to #node.js and #openssl on Freenode!

@prashanth5429
Copy link

Hi,

We are implementing similar application with reference from: https://goo.gl/V6tDA1. But the problem is how can we protect our certificates(.pem files). Any way we can secure them from anyone who has access to the folder except the application using it? or is it possible to store this in windows certificate store and make this application get the keys from the store? Kindly advice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment