How to fix NodeJS “CERT_UNTRUSTED” Issues

Have you ever encountered the niggling “CERT_UNTRUSTED” or “UNABLE_TO_VERIFY_LEAF_SIGNATURE” errors in NodeJS? It is probably one of the most annoying and frustrating issues you’ll ever encounter in Node. When does this issue occur and why? Well whenever you try to access a secure URL/endpoint using HTTPS from your NodeJS app but the Certificate Authorities/CAs (usually the Intermediate ones) in the Certificate Chain used to create the SSL certificate used by the secure URL wasn’t bundled properly (Ughhh!). Yea, Annoying right? Of course NodeJS, being a JavaScript-based web server technology, will thus try to protect your app from a man-in-the-middle attack and complain that it does not trust the SSL certificate. So don’t go cursing Node now, it’s just trying to protect you.

The Wrong Way

So what’s the solution? You’ll likely surf around the Net for solutions and hear a lot of folks telling you that you should modify the HTTPS header options to set the RejectUnauthorized property to false in NodeJS. Like so:


var https = require('https');
var options = {
hostname: 'encrypted.google.com',
port: 443,
path: '/',
method: 'GET',
rejectUnauthorized: false
};
var req = https.request(options, function(res) {
}

view raw

thewrongway.js

hosted with ❤ by GitHub

However that is a very, very bad idea, especially if you are writing production level code. Essentially by setting the RejectUnauthorized property to false, you are in effect allowing ALL certificates to be accepted by NodeJS, and thus making a very big hole in the security of your application. So please, don’t be like the other numb skulls who put this in their production code!

The Right Way

Fortunately, for every wrong way, there are several right ways of doing things and you can find many other more secure solutions to resolve this issue. The recommended way to resolve this is by simply including a local copy of the SSL certificate or the certificates of the CAs in the certificate chain, within your NodeJS app and configuring that in the HTTPS header options. In that way, Node will have something to evaluate the SSL certificate chain against, and thus be able to trust it.   Therefore if you have the actual SSL Certificate you are good to go! You can just configure your HTTPS header options to use it. Like so:


var https = require('https');
var fs = require('fs');
var options = {
hostname: 'encrypted.google.com',
port: 443,
path: '/',
method: 'GET',
key: fs.readFileSync('/ yourapp/certs/key.pem'),
cert: fs.readFileSync('/yourapp/certs/cert.pem'),
rejectUnauthorized: true
};
options.agent = new https.Agent(options);
var req = https.request(options, function(res) {
}

view raw

usingcert.js

hosted with ❤ by GitHub

In the example above we configure the key and cert properties of the header options to use the values of the certificate and key files required to access the secure URL/endpoint.  These files would have already had the certificate chain bundled within them and thus should work fine.

However, if you don’t have access to the actual SSL Certificate, things may get a little stickier. You may need to configure Node with the public certificates of the CAs in the certificate chain.

Thanks to folks like coolaj86 we have node modules that can assist us in downloading the public certificates of most known CAs that we can then use to configure our NodeJS app.  You can check out his GitHub repository at https://github.com/coolaj86/node-ssl-root-cas for further information on how to install and use the module node-ssl-root-cas.

Of course, if you have knowledge of the certificate chain the SSL certificate uses, you could also download the public certificates of the CAs within the certificate chain yourself and configure them in the HTTPS header options without using a node module like node-ssl-root-cas. For example, you could download the public certificate of the Root CA and any Intermediate CAs in the chain and configure them like so:


var https = require('https');
var fs = require('fs');
var options = {
hostname: 'encrypted.google.com',
port: 443,
path: '/',
method: 'GET',
ca: [fs.readFileSync('/yourapp/certs/intCA1Cert.pem'), fs.readFileSync('/yourapp/certs/ rootCACert.pem')],
rejectUnauthorized: true
};
options.agent = new https.Agent(options);
var req = https.request(options, function(res) {
}

Here we configure the ca array property of the header options to use the values of the public certificate files for the Root and Intermediate CAs from the certificate chain of the SSL certificate required to access the secure URL/endpoint. This works just as effectively!

A cleaner way

However there is a cleaner way to do this. You could create a certificate bundle file that includes your certificate and the public certificates of the CAs (i.e. Root and Intermediates) that your SSL certificate uses. You can then configure the HTTPS headers for Node to use the bundle file when making requests. Like so:


var https = require('https');
var fs = require('fs');
var options = {
hostname: 'encrypted.google.com',
port: 443,
path: '/',
method: 'GET',
key: fs.readFileSync('/yourapp/certs/privkey.pem'),
cert: fs.readFileSync('/yourapp/certs/bundle.pem'), // a PEM containing the SERVER and ALL INTERMEDIATES
rejectUnauthorized: true
};
options.agent = new https.Agent(options);
var req = https.request(options, function(res) {
}

Here we create a bundle.pem file that contains the SSL certificate and the public certificates of all the Intermediate CAs as well as the Root CA. We then configure the key and cert properties of the HTTPS header options to use the values of the bundle and private key files.

In Conclusion

“CERT_UNTRUSTED” or “UNABLE_TO_VERIFY_LEAF_SIGNATURE” errors are annoying but justifiably so. Don’t let it ruin your day, or your love for the fantastic platform that NodeJS is, or make you feel so frustrated that you go and sacrifice the security of your web app.  There is a reason why you chose to use Secure HTTP and there are good reasons why Node wants to help you enforce that. Never sacrifice the quality of your application trying to fix what are essentially relatively minor issues the wrong way, especially when there are several right ways of fixing them that maintains the security of your application. Small sacrifices may lead to BIGGER problems in the future! I hope the information provided in this blog post can save someone’s day. Cheers!

3 thoughts on “How to fix NodeJS “CERT_UNTRUSTED” Issues

Leave a comment