3

My Node.JS/Express application is getting the following error when attempting an HTTPS GET request from my server code, to an API served by another server (different company, server not owned by us):

CERT_UNTRUSTED

NOTE: I am running these tests from my Linux box using the "localhost" domain.

I tried the steps outlined in this article to create a self-signed temporary certificate, just to get around this problem:

http://www.hacksparrow.com/node-js-https-ssl-certificate.html

However, I still get the error. (Side note: since I created the server with SSL keys loaded using the HTTPS module, the server only responds to HTTPS (https://) URL requests now. The server does not respond anymore to non-HTTPS requests anymore since I configured it to load my SSL PEM files when creating the server. Oddly enough it prints two "listening-to server on port " prompts when it used to print only one).

How can I fix this?

NOTE: The host name property in the options object in the code below has been changed to a "dummy" URL because it is confidential. If you try the URL you will get an error.

Code excerpts:

var https = require('https');
https.globalAgent.options.secureProtocol = 'SSLv3_method';

var httpsOptions = {
    hostname: dummyHostName,
    port: 80,
    method: 'GET',
    path: '/search?text=test',
    headers: {
        // Request JSON response.
        'Content-Type': 'application/json',
        'Upgrade-Insecure-Requests': '1',
        'json': 'true'
    }};

   var httpsReq =
        https.request(httpsOptions,
            function (resHttp) {
                //  This block is never reached due to the error.
            }  

I tried installing the ssl-root-cas NPM package as per this document:

https://github.com/coolaj86/node-ssl-root-cas

But I could not figure out what PEM files I needed to load under the USAGE section in the instructions, which show dummy file names, so I don't think I'm using it properly.

Here is my package list for the app:

├─┬ body-parser@1.13.3
│ ├── bytes@2.1.0
│ ├── content-type@1.0.1
│ ├── depd@1.0.1
│ ├─┬ http-errors@1.3.1
│ │ ├── inherits@2.0.1
│ │ └── statuses@1.2.1
│ ├── iconv-lite@0.4.11
│ ├─┬ on-finished@2.3.0
│ │ └── ee-first@1.1.1
│ ├── qs@4.0.0
│ ├─┬ raw-body@2.1.6
│ │ ├── bytes@2.3.0
│ │ ├── iconv-lite@0.4.13
│ │ └── unpipe@1.0.0
│ └─┬ type-is@1.6.12
│   ├── media-typer@0.3.0
│   └─┬ mime-types@2.1.10
│     └── mime-db@1.22.0
├─┬ cookie-parser@1.3.5
│ ├── cookie@0.1.3
│ └── cookie-signature@1.0.6
├─┬ debug@2.2.0
│ └── ms@0.7.1
├─┬ express@4.13.4
│ ├─┬ accepts@1.2.13
│ │ ├─┬ mime-types@2.1.10
│ │ │ └── mime-db@1.22.0
│ │ └── negotiator@0.5.3
│ ├── array-flatten@1.1.1
│ ├── content-disposition@0.5.1
│ ├── content-type@1.0.1
│ ├── cookie@0.1.5
│ ├── cookie-signature@1.0.6
│ ├── depd@1.1.0
│ ├── escape-html@1.0.3
│ ├── etag@1.7.0
│ ├─┬ finalhandler@0.4.1
│ │ └── unpipe@1.0.0
│ ├── fresh@0.3.0
│ ├── merge-descriptors@1.0.1
│ ├── methods@1.1.2
│ ├─┬ on-finished@2.3.0
│ │ └── ee-first@1.1.1
│ ├── parseurl@1.3.1
│ ├── path-to-regexp@0.1.7
│ ├─┬ proxy-addr@1.0.10
│ │ ├── forwarded@0.1.0
│ │ └── ipaddr.js@1.0.5
│ ├── qs@4.0.0
│ ├── range-parser@1.0.3
│ ├─┬ send@0.13.1
│ │ ├── destroy@1.0.4
│ │ ├─┬ http-errors@1.3.1
│ │ │ └── inherits@2.0.1
│ │ ├── mime@1.3.4
│ │ ├── ms@0.7.1
│ │ └── statuses@1.2.1
│ ├── serve-static@1.10.2
│ ├─┬ type-is@1.6.12
│ │ ├── media-typer@0.3.0
│ │ └─┬ mime-types@2.1.10
│ │   └── mime-db@1.22.0
│ ├── utils-merge@1.0.0
│ └── vary@1.0.1
├─┬ glob@7.0.3
│ ├─┬ inflight@1.0.4
│ │ └── wrappy@1.0.1
│ ├── inherits@2.0.1
│ ├─┬ minimatch@3.0.0
│ │ └─┬ brace-expansion@1.1.3
│ │   ├── balanced-match@0.3.0
│ │   └── concat-map@0.0.1
│ ├─┬ once@1.3.3
│ │ └── wrappy@1.0.1
│ └── path-is-absolute@1.0.0
├── http-status-codes@1.0.6
├─┬ jade@1.11.0
│ ├── character-parser@1.2.1
│ ├─┬ clean-css@3.4.12
│ │ ├─┬ commander@2.8.1
│ │ │ └── graceful-readlink@1.0.1
│ │ └─┬ source-map@0.4.4
│ │   └── amdefine@1.0.0
│ ├── commander@2.6.0
│ ├─┬ constantinople@3.0.2
│ │ └── acorn@2.7.0
│ ├─┬ jstransformer@0.0.2
│ │ ├── is-promise@2.1.0
│ │ └─┬ promise@6.1.0
│ │   └── asap@1.0.0
│ ├─┬ mkdirp@0.5.1
│ │ └── minimist@0.0.8
│ ├─┬ transformers@2.1.0
│ │ ├─┬ css@1.0.8
│ │ │ ├── css-parse@1.0.4
│ │ │ └── css-stringify@1.0.5
│ │ ├─┬ promise@2.0.0
│ │ │ └── is-promise@1.0.1
│ │ └─┬ uglify-js@2.2.5
│ │   ├─┬ optimist@0.3.7
│ │   │ └── wordwrap@0.0.3
│ │   └─┬ source-map@0.1.43
│ │     └── amdefine@1.0.0
│ ├─┬ uglify-js@2.6.2
│ │ ├── async@0.2.10
│ │ ├── source-map@0.5.3
│ │ ├── uglify-to-browserify@1.0.2
│ │ └─┬ yargs@3.10.0
│ │   ├── camelcase@1.2.1
│ │   ├─┬ cliui@2.1.0
│ │   │ ├─┬ center-align@0.1.3
│ │   │ │ ├─┬ align-text@0.1.4
│ │   │ │ │ ├─┬ kind-of@3.0.2
│ │   │ │ │ │ └── is-buffer@1.1.3
│ │   │ │ │ ├── longest@1.0.1
│ │   │ │ │ └── repeat-string@1.5.4
│ │   │ │ └── lazy-cache@1.0.3
│ │   │ ├─┬ right-align@0.1.3
│ │   │ │ └─┬ align-text@0.1.4
│ │   │ │   ├─┬ kind-of@3.0.2
│ │   │ │   │ └── is-buffer@1.1.3
│ │   │ │   ├── longest@1.0.1
│ │   │ │   └── repeat-string@1.5.4
│ │   │ └── wordwrap@0.0.2
│ │   ├── decamelize@1.2.0
│ │   └── window-size@0.1.0
│ ├── void-elements@2.0.1
│ └─┬ with@4.0.3
│   ├── acorn@1.2.2
│   └─┬ acorn-globals@1.0.9
│     └── acorn@2.7.0
├── jsonfile@2.2.3
├─┬ morgan@1.6.1
│ ├── basic-auth@1.0.3
│ ├── depd@1.0.1
│ ├─┬ on-finished@2.3.0
│ │ └── ee-first@1.1.1
│ └── on-headers@1.0.1
├─┬ scribe-js@2.0.4
│ ├── callsite@1.0.0
│ ├── colors@1.1.2
│ ├─┬ mkdirp@0.5.1
│ │ └── minimist@0.0.8
│ └── moment@2.12.0
├─┬ serve-favicon@2.3.0
│ ├── etag@1.7.0
│ ├── fresh@0.3.0
│ ├── ms@0.7.1
│ └── parseurl@1.3.1
└─┬ ssl-root-cas@1.2.2
  ├── bluebird@3.4.6
  └─┬ request@2.74.0
    ├── aws-sign2@0.6.0
    ├── aws4@1.4.1
    ├─┬ bl@1.1.2
    │ └─┬ readable-stream@2.0.6
    │   ├── core-util-is@1.0.2
    │   ├── inherits@2.0.1
    │   ├── isarray@1.0.0
    │   ├── process-nextick-args@1.0.7
    │   ├── string_decoder@0.10.31
    │   └── util-deprecate@1.0.2
    ├── caseless@0.11.0
    ├─┬ combined-stream@1.0.5
    │ └── delayed-stream@1.0.0
    ├── extend@3.0.0
    ├── forever-agent@0.6.1
    ├─┬ form-data@1.0.1
    │ └─┬ async@2.0.1
    │   └── lodash@4.15.0
    ├─┬ har-validator@2.0.6
    │ ├─┬ chalk@1.1.3
    │ │ ├── ansi-styles@2.2.1
    │ │ ├── escape-string-regexp@1.0.5
    │ │ ├─┬ has-ansi@2.0.0
    │ │ │ └── ansi-regex@2.0.0
    │ │ ├─┬ strip-ansi@3.0.1
    │ │ │ └── ansi-regex@2.0.0
    │ │ └── supports-color@2.0.0
    │ ├─┬ commander@2.9.0
    │ │ └── graceful-readlink@1.0.1
    │ ├─┬ is-my-json-valid@2.13.1
    │ │ ├── generate-function@2.0.0
    │ │ ├─┬ generate-object-property@1.2.0
    │ │ │ └── is-property@1.0.2
    │ │ ├── jsonpointer@2.0.0
    │ │ └── xtend@4.0.1
    │ └─┬ pinkie-promise@2.0.1
    │   └── pinkie@2.0.4
    ├─┬ hawk@3.1.3
    │ ├── boom@2.10.1
    │ ├── cryptiles@2.0.5
    │ ├── hoek@2.16.3
    │ └── sntp@1.0.9
    ├─┬ http-signature@1.1.1
    │ ├── assert-plus@0.2.0
    │ ├─┬ jsprim@1.3.0
    │ │ ├── extsprintf@1.0.2
    │ │ ├── json-schema@0.2.2
    │ │ └── verror@1.3.6
    │ └─┬ sshpk@1.10.0
    │   ├── asn1@0.2.3
    │   ├── assert-plus@1.0.0
    │   ├─┬ bcrypt-pbkdf@1.0.0
    │   │ └── tweetnacl@0.14.3
    │   ├── dashdash@1.14.0
    │   ├── ecc-jsbn@0.1.1
    │   ├── getpass@0.1.6
    │   ├── jodid25519@1.0.2
    │   ├── jsbn@0.1.0
    │   └── tweetnacl@0.13.3
    ├── is-typedarray@1.0.0
    ├── isstream@0.1.2
    ├── json-stringify-safe@5.0.1
    ├─┬ mime-types@2.1.11
    │ └── mime-db@1.23.0
    ├── node-uuid@1.4.7
    ├── oauth-sign@0.8.2
    ├── qs@6.2.1
    ├── stringstream@0.0.5
    ├── tough-cookie@2.3.1
    └── tunnel-agent@0.4.3

Robert Oschler
  • 14,153
  • 18
  • 94
  • 227
  • *"I tried the steps outlined in this article to create a self-signed temporary certificate, just to get around this problem..."* - You did not provide the server's certificate. I also don't see where your CA is trusted by the UA. Also see [How do you sign Certificate Signing Request with your Certification Authority](http://stackoverflow.com/a/21340898/608639) and [How to create a self-signed certificate with openssl?](http://stackoverflow.com/q/10175812/608639) It provides a lot of background information on X.509 server certificates, and where the various rules come from. – jww Sep 06 '16 at 05:00
  • *"... in the code below has been changed to a "dummy" URL because it is confidential"* - So I am clear... You hung the box off the Internet for everyone to bang on; but you hid the URL from us when asking for help. Is that correct? – jww Sep 06 '16 at 07:18
  • @jww - re: first comment. I used the steps depicted in the hacksparrow article I linked to to generate the PEM files for my self-signed certificate. re: second comment: "You hung the box...". No I didn't. As I stated in my post the box I am communicating with a foreign API server owned by an external company, not by us. – Robert Oschler Sep 06 '16 at 11:34

1 Answers1

1

If you have a server that is using self signed certificate (or the domain of the server is not same as defined in the URL), than it is just encrypting the data but not identifying itself. That is why node.js will error that request, but if you want to ignore that issue (which basically from your question that is what I understand), you can pass the following property in your httpsOptions:

rejectUnauthorized=false

See more info at:

https://nodejs.org/api/https.html#https_https_request_options_callback

Philip Kirkbride
  • 21,381
  • 38
  • 125
  • 225
sagie
  • 1,744
  • 14
  • 15
  • 1
    ***`rejectUnauthorized=false`*** sounds as bad as ***`VERIFY_PEER_NONE`***. Wouldn't it be better to fix the underlying problem? – jww Sep 06 '16 at 05:01
  • not always possible to fix. for example internal apps inside an organization might not have a valid certificate. testing servers might not have certificates that are valid and match their domain. and so on... it is not as simple to fix or sometimes possible when you are working with organizations and ops doesn't really do whatever you want it to. – sagie Sep 06 '16 at 05:03
  • 1
    *"Not always possible to fix..."* - Sure; but in this case: ***"I am running these tests from my Linux box using the "localhost" domain"***. In this case, I would be interested in knowing how to fix the underlying problem. I bought the book [Node.js the Right Way](https://www.amazon.com/dp/1937785734), and it does not discuss these topics. – jww Sep 06 '16 at 07:13
  • he is calling an api that is deployed on another server. that other server certificate is not setup correctly (either selfsigned or domain doesn't match certificate or so on...) so he shouldn't really fix the issue on his end, only ignore it, because the issue is not really on his end. the issue needs to be fixed on that other api server that is not in his control from what i understood. – sagie Sep 06 '16 at 07:24
  • @sagie - I know about that setting but as stated in that hacksparrow article and others, that leaves your server vulnerable and is considered bad practice. As I said in my post, Chrome is able to communicate with the external API server properly, I would think I could do the same if I had the right configuration. – Robert Oschler Sep 06 '16 at 11:26
  • 1
    than I have to ask if that api server a 3rd party outside of your company network? does your server go via proxy while your pc does not? does that proxy do ssl interception? there are many variables that can impact that. if this is a test server, i suggest to run with that flag and turn it off in production via NODE_ENV=production/development value – sagie Sep 06 '16 at 11:34
  • @sagie - 1) Yes the api server is a 3rd party outside of your company network. 2) My server running on localhost does not go through a proxy. Are you indicating that if my server was running on a "public facing" server (i.e. - not from localhost), it would work? If so, why can Chrome access the api server while my Node.js app running locally can not? Doesn't that indicate some kind of a certificate chain issue? – Robert Oschler Sep 06 '16 at 11:48
  • I figured you are testing against a 3rd party test server which might not have a valid certificate and in prod you will run against another server with valid certificate. also, client certificates are used by the server to auth the clients that connect to it, so don't think its a client cert issue, but your server and your chrome go out to the network differently (you have a proxy which might do things on the way like ssl interception) makes a difference. you might also want to look at the certificate in the chrome when you navigate there. is it valid? what does chrome state about it. – sagie Sep 07 '16 at 03:39