Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

doc: UNABLE_TO_VERIFY_LEAF_SIGNATURE/unable to verify the first certificate error not documented #33705

Closed
1 task done
j3lamp opened this issue Jun 3, 2020 · 13 comments · Fixed by #34213
Closed
1 task done
Labels
doc Issues and PRs related to the documentations. https Issues or PRs related to the https subsystem.

Comments

@j3lamp
Copy link
Contributor

j3lamp commented Jun 3, 2020

📗 API Reference Docs Problem

  • Version: v12.17.0
  • Platform: macOS 10.14.6 Mojave: Darwin Kernel Version 18.7.0 x86_64
  • Subsystem:

Location

HTTPS Module

Affected URL(s):

Problem description

The error "unable to verify the first certificate" with code UNABLE_TO_VERIFY_LEAF_SIGNATURE is not documented making it extremely difficult to fix.

Turns out this was caused by a site not providing a certificate chain. While the error wasn't node's fault the lack of documentation made it look like a bug in node and made fixing the problem extremely difficult.

The true cause was obscured by work configuring certificate stores to explicitly trust the intermediate certificates so web browsers produced no errors. The vast majority of search results suggest disabling security (a terrible idea), the rest point out the NODE_EXTRA_CA_CERTS which is helpful, but I was already using it.

Note: While this isn't actually a security vulnerability the fact that most advice is to turn off certificate verification it can lead people to introduce security vulnerabilities on their own.

Error: unable to verify the first certificate
    at TLSSocket.onConnectSecure (_tls_wrap.js:1496:34)
    at TLSSocket.emit (events.js:315:20)
    at TLSSocket._finishInit (_tls_wrap.js:938:8)
    at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:696:12) {
  code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE'
}

  • I would like to work on this issue and submit a pull request.
@j3lamp j3lamp added the doc Issues and PRs related to the documentations. label Jun 3, 2020
@sam-github
Copy link
Contributor

The cause of this is the server is, incorrectly, not returning the intermediate certificates, its a server configuration problem.

If the server is node, https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options describes how to set a cert chain:

Each cert chain should consist of the PEM formatted certificate for a provided private key, followed by the PEM formatted intermediate certificates (if any), in order, and not including the root CA (the root CA must be pre-known to the peer, see ca).

https://levelup.gitconnected.com/how-to-resolve-certificate-errors-in-nodejs-app-involving-ssl-calls-781ce48daded looks like a decent description of the situation.

A complete tutorial on diagnosing problems with TLS setup, such as the above, would be a deal of work, but worthwhile.

@j3lamp
Copy link
Contributor Author

j3lamp commented Jun 4, 2020

I concur. Once I figured that out the problem was easy to fix. What I hoped to find in Node's docs was a list of errors with descriptions as to what they meant. If there had been an entry for UNABLE_TO_VERIFY_LEAF_SIGNATURE that contained your first sentence that would help a lot of people solve what is currently a difficult problem to solve.

A complete tutorial would be awesome, but I don't think that is a necessary place to begin.

Obviously I don't know where to find a list of possible errors. (-: But if I am pointed in the correct directions I can probably dig up some time to try and write something useful.

@DerekNonGeneric
Copy link
Contributor

Obviously I don't know where to find a list of possible errors.

https://nodejs.org/api/errors.html#errors_node_js_error_codes

@j3lamp, is this what you are looking for?

@j3lamp
Copy link
Contributor Author

j3lamp commented Jun 10, 2020

It looks like it! The questions now is what errors besides UNABLE_TO_VERIFY_LEAF_SIGNATURE need to be added to it: which, for TLS, seems to be answered here:

const char* X509ErrorCode(long err) { // NOLINT(runtime/int)

Now I know what to dig for probably in OpenSSL. Thank you.

@j3lamp
Copy link
Contributor Author

j3lamp commented Jun 11, 2020

I have been thinking about this. Not being a Node.js developer and not having needed to read all of its documentation(!) I am uncertain if these doc should contain OpenSSL errors. On one hand it would be extremely helpful to people who encounter them and would almost certainly make web searches like "node.js error UNABLE_TO_VERIFY_LEAF_SIGNATURE" produce useful results on the first page. On the other hand these aren't Node.js' errors so why should Node.js have to document them and keep them up to date as OpenSSL changes?

I think I have found enough that I can add these errors to Node.js' docs, but I don't believe I can decide if they should be added.

j3lamp added a commit to j3lamp/node that referenced this issue Jul 6, 2020
j3lamp added a commit to j3lamp/node that referenced this issue Jan 27, 2021
@atulrawat85

This comment was marked as spam.

@j3lamp
Copy link
Contributor Author

j3lamp commented Feb 21, 2021

The suggestion linked to above will work, however if you use rejectUnauthorized: false then certificates won't be verified. It is much better to use NODE_EXTRA_CA_CERTS environment variable to add the other root certificates that should be trusted.

@thw0rted
Copy link
Contributor

thw0rted commented Sep 4, 2023

I'm still not sure how this isn't an error with Node. I can open a TLS connection to a server that doesn't return intermediate certificates in their handshake, and it works fine from every browser I've tried, plus curl, as long as they trust the actual root. Is this a case of other clients (browsers, curl) implementing the spec "loosely" while Node is strict? If so, can Node maybe start behaving like everybody else?

ETA: Looks like I'm asking for #16336 again. Sorry!

ETA again: aaand that issue has no updates for almost 4 years. Might be worth opening a new one?

@bnoordhuis
Copy link
Member

#16336 is about something else (unless its requester was asking what you're asking but phrased it poorly.)

The behavior you want is controlled by the X509_V_FLAG_PARTIAL_CHAIN flag, which node currently doesn't set. That kind of policy preferably comes from upstream and I don't think there's consensus it's a good default (although the discussion partially hinges on backwards compatibility): openssl/openssl#7871

That said, curl sets it so... maybe it's alright? The change itself is trivial, feel free to open a PR.

diff --git a/src/crypto/crypto_context.cc b/src/crypto/crypto_context.cc
index 3876adf7d7..dfbbca27bf 100644
--- a/src/crypto/crypto_context.cc
+++ b/src/crypto/crypto_context.cc
@@ -235,6 +235,8 @@ X509_STORE* NewRootCertStore() {
     }
   }
 
+  CHECK_EQ(1, X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN));
+
   return store;
 }
 

@thw0rted
Copy link
Contributor

thw0rted commented Sep 4, 2023

You're right, I didn't explain well.

I'm trying to make a request to a server that does not send any part of its chain -- you get the subject certificate, but no intermediates and no root. In browsers, this still works if you've ever visited another site that was signed by the same intermediate cert, because as far as I can tell all major browsers cache intermediate certs and will quietly include them when trying to compute a trusted chain.

I believe, but can't find supporting documentation, that some (?) browsers may also proactively reach out to download the issuing (intermediate) CA cert from its canonical URI if it isn't included with the handshake, then presumably do the same going up the chain until it hits either a trusted anchor or something self-signed.

The former behavior isn't much help for Node, since the chances of making one request to a server that includes intermediate CAs followed by another that fails to do so, but uses a cert that happens to be signed by an intermediate from the first request, sounds pretty unlikely. The latter behavior is what they're suggesting in #16336 and seems like it would have a better potential of "just working". My main goal is to avoid writing custom code to handle this case -- I don't run the server in question, I just want to be able to make requests to it without choosing between totally disabling CA checks or going out and finding the intermediate CAs myself. It'd be nice if Node did that legwork for me, to the extent possible.

@bnoordhuis
Copy link
Member

Right, but I'd say the chances of download-on-demand getting implemented are close to zero. It just doesn't fit well with node's philosophy. It's best to think of node as a network toolkit, not an end user product like browsers are.

@thw0rted
Copy link
Contributor

thw0rted commented Sep 5, 2023

I'm having trouble finding another example of an incomplete cert chain -- the one linked from #16336 on badssl.com is now expired, as well as incomplete -- but the company-internal one that brought me here works from browsers as well as curl, only Node fails.

I also think of Node as a network toolkit, but I like my network tools to Just Work where possible. I can set up a bundle of root CAs once, configure NODE_EXTRA_CA_CERTS (and CURL_CA_BUNDLE) to point to it, then I'd like to be able to get on with writing code. I don't want to have to track down every intermediate CA that was issued and shove that in the bundle, and I don't want to put work on hold while I file a ticket with the server admins to fix their chains. I just think that's a pretty good case for Node implementing friendlier DX. You don't have to be an "end user product" to go the extra mile trying to prevent an avoidable error.

@bnoordhuis
Copy link
Member

You're welcome to open a pull request and see how it's received.

@aduh95 aduh95 closed this as completed in e22bc1e May 5, 2024
Ch3nYuY pushed a commit to Ch3nYuY/node that referenced this issue May 8, 2024
Fixes: nodejs#33705
PR-URL: nodejs#34213
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
targos pushed a commit that referenced this issue May 8, 2024
Fixes: #33705
PR-URL: #34213
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
marco-ippolito pushed a commit that referenced this issue Jun 17, 2024
Fixes: #33705
PR-URL: #34213
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
sophoniie pushed a commit to sophoniie/node that referenced this issue Jun 20, 2024
Fixes: nodejs#33705
PR-URL: nodejs#34213
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
bmeck pushed a commit to bmeck/node that referenced this issue Jun 22, 2024
Fixes: nodejs#33705
PR-URL: nodejs#34213
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
doc Issues and PRs related to the documentations. https Issues or PRs related to the https subsystem.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants