Srijan Choudhary
Srijan Choudhary

Reputation: 480

How to make Node.js/Express.js deny a connection based on vhost, before ssl validation

My setup is something like this:

[Apache running on port 80]
http://domain1/  --->   https://domain1/   (redirect using apache)
http://domain2/  --->   Hosted on apache.

and

[Nodejs running on port 443]
https://domain1/ --->   App on Express.js/Node.js

What I want now is that any requests to https://domain2/ should be denied connection.

My ssl certificate is only valid for domain1. So, I cannot do something like this:

var vhostController = express.createServer({key:privateKey,cert:certificate});
vhostController.use(express.vhost('domain1',mainApp));
vhostController.use(express.vhost('domain2',function(req, res) {res.end();}));
vhostController.listen(443);

The above code works for http, but not for https, because it tries to validate the server certificate before passing to the function which handles what to return.

So, is there some way to check before the certificate error that the request was for domain2, and deny/close the connection.

Upvotes: 0

Views: 2191

Answers (3)

Srijan Choudhary
Srijan Choudhary

Reputation: 480

This problem has 2 solutions:

  1. Using different IPs: Works everywhere.
  2. Using SNI: Does not require different IPs, but does not work in all clients (more info at https://sni.velox.ch/).

Using different IPs:

I ended up using this method. Sample Code:

var app = express.createServer({key:privateKey,cert:certificate});
// setup app..
app.listen(443,IP1);

And I don't run anything on IP2 on port 443. So, any connection to IP2 is rejected.

Using SNI:

If someone wants to use SNI, details about using it with nodejs can be found here:

http://nodejs.org/docs/latest/api/tls.html#event_secureConnection_

In the secureConnection event of tls server, a cleartextStream variable is passed, in which, if the client supports SNI, cleartextStream.servername gives the server domain wanted by the client. It can be checked here and appropriate action can be taken.

Upvotes: 1

In most cases, no. SSL handshake occurs before HTTP request is sent (actually HTTP request is sent over established SSL channel). TLS 1.0 protocol specifies special TLS extension, that lets the client tell the server, certificate for which host to present. Unfortunately, not all (I'd say few) clients support this extension so your problem has no universal solution.

Upvotes: 3

alessioalex
alessioalex

Reputation: 63653

An elegant way for this would be to create a middleware function, like so:

function kill_it(res) {
  res.end(); // empty response
}

module.exports = function denyVhost(vhostArray) {
  return function(req, res, next) {
    var host;
    if (req.headers.host) {
      host = req.headers.host.split(':')[0];
      if (vhostArray.indexOf(host) > -1) {
        next(); // the host is found
      } else {
        kill_it(res);
      }
    } else {
      // the host isn't found in the request headers
      kill_it(res);
    }
  }
};

Then in your express configuration you can use it like so:

deny_by_vhost = require('./example_above');

app.configure(function() {
  ...
  app.use(deny_by_vhost(["example.com", "foo.example.com"]));
  ...
});

Upvotes: 0

Related Questions