414

I'm working on a little app that logs into my local wireless router (Linksys) but I'm running into a problem with the router's self-signed ssl certificate.

I ran wget 192.168.1.1 and get:

ERROR: cannot verify 192.168.1.1's certificate, issued by `/C=US/ST=California/L=Irvine/O=Cisco-Linksys, LLC/OU=Division/CN=Linksys/emailAddress=support@linksys.com':
Self-signed certificate encountered.
ERROR: certificate common name `Linksys' doesn't match requested host name `192.168.1.1'.
To connect to 192.168.1.1 insecurely, use `--no-check-certificate'.

In node, the error being caught is:

{ [Error: socket hang up] code: 'ECONNRESET' }

My current sample code is:

var req = https.request({ 
    host: '192.168.1.1', 
    port: 443,
    path: '/',
    method: 'GET'

}, function(res){

    var body = [];
    res.on('data', function(data){
        body.push(data);
    });

    res.on('end', function(){
        console.log( body.join('') );
    });

});
req.end();

req.on('error', function(err){
    console.log(err);
});

How can I go about getting node.js to do the equivalent of "--no-check-certificate"?

Geuis
  • 41,122
  • 56
  • 157
  • 219
  • 1
    For everyone coming here in the fourth year of the pandemic, the answer is: [`NODE_EXTRA_CA_CERTS`](https://nodejs.org/api/cli.html#node_extra_ca_certsfile). Shaking my head, hundreds of bad responses and the only one that mentions this has 2 votes. [Upvote this!](https://stackoverflow.com/a/64885078/) – Ahmed Fasih Mar 28 '23 at 13:57

13 Answers13

795

Cheap and insecure answer:

Add

process.env["NODE_TLS_REJECT_UNAUTHORIZED"] = 0;

in code, before calling https.request()

A more secure way (the solution above makes the whole node process insecure) is answered in this question

evandrix
  • 6,041
  • 4
  • 27
  • 38
Juanra
  • 8,532
  • 2
  • 15
  • 12
  • 2
    Worked like a charm for me! I placed this code right after I included everything in the very top of my main application js. – Xedecimal Sep 20 '14 at 00:33
  • This also worked for NodeJS & SailJS combo. I added it at the top of local.js – Michael Kork. Sep 29 '14 at 15:01
  • 51
    Do not use this or "rejectUnauthorized" in a production environment, as this disables all kinds of security checks. – Jason Walton Jan 24 '15 at 02:17
  • 4
    I was having trouble with running tests using mocha on my self-signed https node server, and adding this immediately before any describe blocks made my tests pass. – artis3n Jul 30 '15 at 14:33
  • This is probably not the safest way to fix the problem. See http://stackoverflow.com/questions/20433287/node-js-request-cert-has-expired#answer-29397100 – Matt Pennington Jan 27 '16 at 03:15
  • For people who say like sharm what sharm when you get hit by an MITM atack as it ignors the server certificat, this answer has 3 years and still misleading !!!!!! am i missing something guys – achabahe Feb 01 '18 at 20:23
  • i tested this code with a sevrer that has a certificat that is not specified in the ca option and it connected and gave me the socket object now imagine that this server is the MITM !!!!!do you imagine the horror he will summon and the CHAOS he will bring while you are thniking every thing is fine – achabahe Feb 01 '18 at 20:37
  • 5
    Well this is intended for testing purposes only. You should not use this in production. As stated in the answer, it's not the safest way to overcome the problem – Juanra Feb 02 '18 at 20:50
  • It worked however, it is not secured method but obviously you can test things out at local environment. – Haisum Usman May 23 '19 at 19:03
  • Be sure you read [this article](https://medium.com/@jonatascastro12/understanding-self-signed-certificate-in-chain-issues-on-node-js-npm-git-and-other-applications-ad88547e7028) first before you shoot yourself in the foot. – Ognyan Dimitrov Feb 07 '20 at 08:18
  • 1
    Nitpick: this should be a string `'0'` rather than a number `0`. Env vars are always strings, and TypeScript complains as such. – jameshfisher Jan 21 '23 at 17:28
196

In your request options, try including the following:

   var req = https.request({ 
      host: '192.168.1.1', 
      port: 443,
      path: '/',
      method: 'GET',
      rejectUnauthorized: false,
      requestCert: true,
      agent: false
    },
Meg Sharkey
  • 2,085
  • 1
  • 11
  • 4
  • 2
    Worked for me. I use restler and I see it did not forward the options by default so I had to patch it. – Olivier Amblet Apr 26 '13 at 14:18
  • 2
    For this to work you need to provide an explicit instance of a custom Agent. Create the options object and set the agent: 'options.agent = new https.Agent(options);' Then just call 'https.request(options)' – Max Jul 27 '15 at 12:55
  • 24
    Well, this worked for me with just the `rejectUnauthorized` option and nothing else – mcont Jul 29 '16 at 14:08
  • 1
    @mcont i confirm just `rejectUnauthorized` was good enough everything else ootb. Using within vs code extension. Better yet allow PEM configuration, i'll do that next... – escape-llc May 26 '20 at 12:08
  • requestCert is for server – Ali Aug 26 '21 at 12:23
79

Don't believe all those who try to mislead you.

In your request, just add:

ca: [fs.readFileSync([certificate path], {encoding: 'utf-8'})]

If you turn on unauthorized certificates, you will not be protected at all (exposed to MITM for not validating identity), and working without SSL won't be a big difference. The solution is to specify the CA certificate that you expect as shown in the next snippet. Make sure that the common name of the certificate is identical to the address you called in the request(As specified in the host):

What you will get then is:

var req = https.request({ 
      host: '192.168.1.1', 
      port: 443,
      path: '/',
      ca: [fs.readFileSync([certificate path], {encoding: 'utf-8'})],
      method: 'GET',
      rejectUnauthorized: true,
      requestCert: true,
      agent: false
    },

Please read this article (disclosure: blog post written by this answer's author) here in order to understand:

  • How CA Certificates work
  • How to generate CA Certs for testing easily in order to simulate production environment
glennsl
  • 28,186
  • 12
  • 57
  • 75
Hesham Yassin
  • 4,341
  • 2
  • 21
  • 23
  • 9
    This works and is the right way of fixing the problem "Error: self signed certificate in certificate chain." – RohanRasane Feb 02 '18 at 07:26
  • 1
    why do you put fs.readFileSync inside brackets, instead of storing it as a string? – Lelo Jan 18 '19 at 19:37
  • Lelo: brackets turn it into an array. ca: expects an array of certs. This file should be a comma separated list of certs, often people use an inner function to turn a PEM file into an array. For a self signed cet a single cert "should" work. – JohnDavid Feb 14 '20 at 20:20
  • This snippet still has "rejectUnauthorized: true". How is this different to the other answers that are disabling cert authorisation by way of rejectUnauth. – spryce May 08 '22 at 23:47
  • @spryce `rejectUnauthorized: true` *enables* ssl verification. – Gershom Maes Jan 25 '23 at 18:50
  • This seems like the "right" approach in a void, it won't help the OP - the CN in the certificate ("linksys") won't be recognized as 192.168.1.1 (unless a custom DNS configuration does it in the OP network). ISelf signed certs are used to provision test system with dynamic IPs, or to provide TLS support for devices that will be deployed with no upfront knowledge of the CN used to access them. In this case, using the option `rejectUnauthorized: false` would be the way to go, provided the developer limits it to those "dynamic" resources and accept the risk that goes with it... – Francois Connetable Jun 01 '23 at 13:32
76

Add the following environment variable:

NODE_TLS_REJECT_UNAUTHORIZED=0

e.g. with export:

export NODE_TLS_REJECT_UNAUTHORIZED=0

(with great thanks to Juanra)

Armand
  • 23,463
  • 20
  • 90
  • 119
24

Adding to @Armand answer:

Add the following environment variable:

NODE_TLS_REJECT_UNAUTHORIZED=0 e.g. with export:

export NODE_TLS_REJECT_UNAUTHORIZED=0 (with great thanks to Juanra)

If you on windows usage:

set NODE_TLS_REJECT_UNAUTHORIZED=0

Thanks to: @weagle08

hackp0int
  • 4,052
  • 8
  • 59
  • 95
16

You can also create a request instance with default options:

require('request').defaults({ rejectUnauthorized: false })
Eduardo
  • 5,645
  • 4
  • 49
  • 57
  • Thanks this helped a lot also with "Error: unable to get local issuer certificate" while logging in to surge – Tobi Sep 26 '20 at 14:03
6

So, my company just switched to Node.js v12.x. I was using NODE_TLS_REJECT_UNAUTHORIZED, and it stopped working. After some digging, I started using NODE_EXTRA_CA_CERTS=A_FILE_IN_OUR_PROJECT that has a PEM format of our self signed cert and all my scripts are working again.

So, if your project has self signed certs, perhaps this env var will help you.

Ref: https://nodejs.org/api/cli.html#cli_node_extra_ca_certs_file

wayneseymour
  • 391
  • 4
  • 5
  • 1
    This answer works best if you can provide the root CA, much better than just disabling cert checking altogether. – datu-puti May 22 '23 at 09:17
4

try export NODE_TLS_REJECT_UNAUTHORIZED=0

3

For meteorJS you can set with npmRequestOptions.

HTTP.post(url, {
    npmRequestOptions: {
        rejectUnauthorized: false // TODO remove when deploy
    },
    timeout: 30000, // 30s
    data: xml
}, function(error, result) {
    console.log('error: ' + error);
    console.log('resultXml: ' + result);
});
digz6666
  • 1,798
  • 1
  • 27
  • 37
1

Or you can try to add in local name resolution (hosts file found in the directory etc in most operating systems, details differ) something like this:

192.168.1.1 Linksys 

and next

var req = https.request({ 
    host: 'Linksys', 
    port: 443,
    path: '/',
    method: 'GET'
...

will work.

nalply
  • 26,770
  • 15
  • 78
  • 101
piaf
  • 11
  • 1
  • 3
    true that this might answer the question but I think the next error will be DEPTH_ZERO_SELF_SIGNED_CERT in this case. – Olivier Amblet Apr 26 '13 at 14:24
  • 1
    so how does one get around DEPTH_ZERO_SELF_SIGNED_CERT? I am running into that now. – reza Oct 31 '13 at 15:44
  • 3
    @reza: add this to your options: `rejectUnauthorized: false` – Obay Mar 20 '14 at 03:40
  • 1
    I know this is a little old but for future reference (in order to do this the correct way), you need to get a PEM-encoding of the self-signed certificate and include it in the options as a CA (you apparently also need to set the agent value but that can be false). Since the certificate is self-signed, it acts as its own CA and therefore can be used to verify itself. However I would also question whether it would really be worth it to do on a router since the firmware could probably be downloaded and therefore the private key could be easily compromised. – Jonathan Gray Dec 09 '14 at 14:36
1

When you cannot control the request creation

When using packages you sometimes don't have the option to set the correct settings on the request call, nor does the package offer you a way to inject a request.

However you might still want to avoid the insecure NODE_TLS_REJECT_UNAUTHORIZED=0 and opt for only having an insecure connection to a specified target.

This is how I solved the issue:

// check if host and port fit your application
function isSelf(host, port) {
  return host === myHost && port === myPort;
}

// get the built in tls module and overwrite the default connect behavior 
const tls = require("tls");
const _connect = tls.connect;
function wrappedConnect(options, secureConnectListener) {
  if (isSelf(options.host, options.port)) {
    options.rejectUnauthorized = false;
  }
  return _connect(options, secureConnectListener);
}
tls.connect = wrappedConnect;
1

In case you are looking for posting using @nestjs/axios,

here is the syntax without certificate (Non Production Solution):

const token = Buffer.from(`${user}:${password}`,'utf8').toString('base64')

const config = {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Basic ${token}`,
      },
      httpsAgent: new https.Agent({
        rejectUnauthorized: false
      }),
    };

const responseData = await firstValueFrom(
        this.httpService.post(url, data, config).pipe(map((response) => response.data)),
      );

here is the syntax with certificate (Production Solution):

const token = Buffer.from(`${user}:${password}`,'utf8').toString('base64')

const config = {
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Basic ${token}`,
      },
      httpsAgent: new https.Agent({
        rejectUnauthorized: true,
        ca: fs.readFileSync(path.join(__dirname, './resources/certificateName'))
      }),
    };

const responseData = await firstValueFrom(
        this.httpService.post(url, data, config).pipe(map((response) => response.data)),
      );
iravinandan
  • 659
  • 6
  • 16
0

If you are using 'aws-sdk' then update your endpoint config like this

const endpoint = new AWS.Endpoint(ELASTICSEARCH_DOMAIN, {
  sslEnabled: false, // this is to off ssl
});
const request = new AWS.HttpRequest(endpoint, AWS_REGION);

I found it by reading the library files Code from HTTP file from aws-sdk lib

Docs are not much help, even here is link https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Config.html#sslEnabled-property I hope this works for you