4

When running a server on a non-standard port, there's a chance of a client incorrectly using HTTP instead of HTTPS.

I would like to return an HTTP 301 redirect if that's detected.

NodeJS does raise a helpful error in that case, but writing a response in that error handler doesn't go through to the client (I believe the socket is already destroyed at that point).

const https = require('https');
const fs = require('fs');

const port = 9000;
const options = {
  key: fs.readFileSync('key.pem'),
  cert: fs.readFileSync('cert.pem')
};

server = https.createServer(options, function (req, res) {
  res.writeHead(200);
  res.end("hello world\n");
}).listen(port);

server.on('clientError', (err, conn) => {
  if (err.code == "ERR_SSL_HTTP_REQUEST") {
    console.warn("HTTP request was sent to HTTPS port");
    conn.end('HTTP/1.1 301 Moved Permanently\r\nLocation: https://127.0.0.1:8443')
  }
});

Sending an HTTP request with curl -v http://127.0.0.1:9000 just gives Empty reply from server

.

As an interesting aside. The error code ERR_SSL_HTTP_REQUEST appears in zero Google Search results. Anyone know how that code gets generated?

idle
  • 1,117
  • 9
  • 17
  • 3
    You can't have both an http and https server on the same port and it's a security risk to ever allow an http connection, even just to return a redirect on the secure port so it simply isn't done that way. The usual back-stop for an accidental http request is to put an http server on port 80 so if the user just does `http://yourdomain.com`, then they will get redirected to the https server and port. – jfriend00 Feb 05 '20 at 22:58
  • I certainly agree for standard port 80/443 configs, but the situation I'm describing is an SSL webapp running on a non-standard port. I'm not arguing it's a good idea, just trying to figure out if it's possible. – idle Feb 05 '20 at 23:13
  • 1
    There are apparently some https server implementation that will do this. I've not found anything that indicates that the nodejs https server will. For an example, go to [http://www.google.com:443](http://www.google.com:443). It redirects you. Note that was `http://` on port 443. – jfriend00 Feb 05 '20 at 23:13
  • We are officially the one and only Google result for `ERR_SSL_HTTP_REQUEST` – idle Feb 05 '20 at 23:15
  • 1
    Google is returning this response: `HTTP/1.1 307 Internal Redirect\nLocation: https://www.google.com/\nNon-Authoritative-Reason: HSTS` when you connect to [http://www.google.com:443](http://www.google.com:443). – jfriend00 Feb 05 '20 at 23:16
  • 1
    Are you saying that you actually get the `"ERR_SSL_HTTP_REQUEST"` error string in your code? I can't even find that string anywhere in the node.js code repository. – jfriend00 Feb 05 '20 at 23:22
  • 2
    From reading a few other articles, this requires special treatment deep inside the TLS library at the point it realizes this isn't a TLS connection and appears to be a plain http connection and it is not something one can do from the outside unless the TLS implementation detects that situation and specifically lets you hook it. – jfriend00 Feb 05 '20 at 23:24
  • 1
    It sounds like nginx can do it per [this part of their doc](http://nginx.org/en/docs/http/configuring_https_servers.html#single_http_https_server). – jfriend00 Feb 05 '20 at 23:26
  • 2
    A fair amount of old discussion on the topic here: [Automatic https redirection](https://stackoverflow.com/questions/7450940/automatic-https-connection-redirect-with-node-js-express). Ignore all the answers that deal with the dual port situation. Some answers address what you're asking about. The only conclusion I saw there was to put NGINX in front of your nodejs server to handle it there. – jfriend00 Feb 05 '20 at 23:32
  • 2
    And, pretty much a duplicate question here [Nodejs http and https over same port](https://stackoverflow.com/questions/22453782/nodejs-http-and-https-over-same-port/23975955#23975955). The only reason I don't yet close yours as a dup is that was a 2014 question so I'm allowing some time to discover something more recent. There is a proxy-based solution where you have an http server running on that port and you sniff the first few bytes and try to figure out whether it's http or https and then proxy to an appropriate type server on some different internal port (that would be invisible). – jfriend00 Feb 05 '20 at 23:34
  • Re: the note about `ERR_SSL_HTTP_REQUEST` string. YES! I do hit that case with my sample ` [Error: 9472:error:1408F09C:SSL routines:ssl3_get_record:http request:c:\ws\deps\openssl\openssl\ssl\record\ssl3_record.c:322: ] { library: 'SSL routines', function: 'ssl3_get_record', reason: 'http request', code: 'ERR_SSL_HTTP_REQUEST' } ` I agree it's a mystery where that is defined – idle Feb 06 '20 at 00:14
  • Presumably could one sniff the first data packet in node for something HTTP-like (ie starting with ['G','E','T',' ']) prior to TLS taking over? – idle Feb 06 '20 at 00:18
  • Actually I guess that output from console.dir(err) above is pretty telling about where the error code is generated – idle Feb 06 '20 at 00:19
  • It just seems such a shame that it feels 99% of the way there. The right error is detected for that case, but the socket is closed just a tiny bit too early to make use of it :-\ – idle Feb 06 '20 at 00:31
  • Here is the "real" error for openssl, which gives more interesting search results. https://github.com/openssl/openssl/blob/master/ssl/ssl_err.c#L180 – idle Feb 06 '20 at 00:52
  • 1
    Yeah, I think part of the issue is that nodejs relies on openSSL which does not apparently have an objective to do what you want. – jfriend00 Feb 06 '20 at 00:59

0 Answers0