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

how to handle self-signed certificates? #14

Open
matdombrock opened this issue Apr 30, 2023 · 6 comments
Open

how to handle self-signed certificates? #14

matdombrock opened this issue Apr 30, 2023 · 6 comments
Labels
question Further information is requested

Comments

@matdombrock
Copy link

When I try to run a basic client example I get this error:

Error: self-signed certificate
    at TLSSocket.onConnectSecure (node:_tls_wrap:1540:34)
    at TLSSocket.emit (node:events:513:28)
    at TLSSocket._finishInit (node:_tls_wrap:959:8)
    at TLSWrap.ssl.onhandshakedone (node:_tls_wrap:743:12) {
  code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
}

Full client.js

const request = require('@derhuerst/gemini/client')

const opt = {
	// follow redirects automatically
	// Can also be a function `(nrOfRedirects, response) => boolean`.
	followRedirects: false,
	// client certificates
	useClientCerts: false,
	letUserConfirmClientCertUsage: null,
	//clientCertStore: defaultClientCertStore,
	// time to wait for socket connection & TLS handshake
	connectTimeout: 60 * 1000, // 60s
	// time to wait for response headers *after* the socket is connected
	headersTimeout: 30 * 1000, // 30s
	// time to wait for the first byte of the response body *after* the socket is connected
	timeout: 40 * 1000, // 40s
	// additional options to be passed into `tls.connect`
	tlsOpt: {},
	// verify the ALPN ID chosen by the server
	// see https://de.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation
	verifyAlpnId: alpnId => alpnId ? (alpnId === ALPN_ID) : true,
};

request('gemini://gemini.circumlunar.space/', opt, (err, res) => {
    console.log(opt);
	if (err) {
		console.error(err)
		process.exit(1)
	}

	console.log(res.statusCode, res.statusMessage)
	if (res.meta) console.log(res.meta)
	res.pipe(process.stdout)
})

Environment

# Node
v18.16.0
# NPM
v9.6.5

Resolution

I was able to get requests working by adding the following line to the top of client.js.

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

I'm not totally sure why this fixes it because I don't understand the root problem. But this works on my end.

Source for solution:
https://stackoverflow.com/questions/23601989/client-certificate-validation-on-server-side-depth-zero-self-signed-cert-error

@derhuerst derhuerst added the question Further information is requested label May 1, 2023
@derhuerst
Copy link
Owner

It seems like gemini://gemini.circumlunar.space/ uses a self-signed SSL certificate. By default – and @derhuerst/gemini does not change these defaults – Node.js rejects certificates that are not tied to one from its list of CA certificates.

This behaviour can be changed with tls.connects()'s rejectUnauthorized: false option; It can be passed into @derhuerst/gemini as tlsOpt: {rejectUnauthorized: false}. This will effectively allow any certificate, so it exposes you to MITM attacks.

The proper way to solve this is to use tls.connects()'s checkServerIdentity() option in order to store the certificate's footprint and then later compare it (TOFU).

@derhuerst
Copy link
Owner

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

I'm not totally sure why this fixes it because I don't understand the root problem. But this works on my end.

While your solution does allow any certificate for all Gemini servers (which is insecure, see above), it also disables all certificate checks in all other encrypted network communication that your Node.js script does (DB connection, calls to 3rd-party APIs, etc)!

@derhuerst
Copy link
Owner

Does that answer your question?

@derhuerst derhuerst changed the title Error: self-signed certificate how to handle self-signed certificates? May 1, 2023
@matdombrock
Copy link
Author

From what I understand, self signed certs are the norm for most Gemini capsules.

The proper way to solve this is to use tls.connects()'s checkServerIdentity() option in order to store the certificate's footprint and then later compare it (TOFU).

It might make sense for that to be the default behavior if possible. Or at least add an example of this use case.

@derhuerst
Copy link
Owner

From what I understand, self signed certs are the norm for most Gemini capsules.

Huh, still it feels weird to turn off the cert validation by default. Without another reasonably effective way to know some host's cert or at least TOFU, this is almost like dropping all encryption, as it allows anyone to MITM the connection.

The proper way […] to store the certificate's footprint and then later compare it (TOFU).

It might make sense for that to be the default behavior if possible. Or at least add an example of this use case.

I agree, this should be documented; Having an example which demonstrates how to implement server cert TOFU using e.g. plain text files or an SQLite DB would be great. PR welcome!

Given that @derhuerst/gemini is intended to be a low-level und environment-agnostic implementation of the protocol, an actual implementation should IMO be in a library on top of it, e.g. gemini-fetch. @RangerMauve What do you think?

@derhuerst
Copy link
Owner

@matdombrock Would you mind submitting a PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants