Post

NodeJS Express SNI

In this post we’ll look at how to add SNI (Server Name Indication) to an express HTTPS server. This will allow us to return different certificates based on the domain (server name).

This post will assume that you already know how to setup a HTTPS server using express. If you don’t know how to, you can learn here.

  1. SNI Callback Function
  2. Server Options
  3. Full Code

SNI Callback Function

The first step will be to create a function that will be called when a new TLS connection is received. This function decides which certificate and private key to use based on the server name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const sniDefaultCert = fs.readFileSync(path.join(__dirname, 'certs/normal.crt'));
const sniDefaultKey = fs.readFileSync(path.join(__dirname, 'certs/normal.key'));

const sniCallback = (serverName, callback) => {
	let cert = null;
	let key = null;

	if (serverName === 'testing.com') {
		cert = fs.readFileSync(path.join(__dirname, 'certs/testing.crt'));
		key = fs.readFileSync(path.join(__dirname, 'certs/testing.key'));
	} else {
		cert = sniDefaultCert;
		key = sniDefaultKey;
	}

	callback(null, new tls.createSecureContext({
		cert,
		key,
	}));
}
  • When a connection is received for testing.com, the testing.crt cert will be used.
  • The normal.crt cert will be used for all other domains.

Server Options

To enable SNI, we need to add the key SNICallback to the server options Object. The value will be a callback that will be invoked when a new TLS connection is received.

1
2
3
4
5
6
7
const serverOptions = {
	SNICallback: sniCallback,

	// Optional: TLS Versions
	maxVersion: 'TLSv1.3',
	minVersion: 'TLSv1.2'
}

Full Code

1
npm i express
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const express = require('express');
const path = require('path');
const fs = require('fs');
const tls = require('tls');
const app = express();

const PORT = 8443;

const sniDefaultCert = fs.readFileSync(path.join(__dirname, 'certs/normal.crt'));
const sniDefaultKey = fs.readFileSync(path.join(__dirname, 'certs/normal.key'));

const sniCallback = (serverName, callback) => {
	console.log(serverName);
	let cert = null;
	let key = null;

	if (serverName === 'testing.com') {
		cert = fs.readFileSync(path.join(__dirname, 'certs/testing.crt'));
		key = fs.readFileSync(path.join(__dirname, 'certs/testing.key'));
	} else {
		cert = sniDefaultCert;
		key = sniDefaultKey;
	}

	callback(null, new tls.createSecureContext({
		cert,
		key,
	}));
}

const serverOptions = {
	SNICallback: sniCallback,

	// Optional: TLS Versions
	maxVersion: 'TLSv1.3',
	minVersion: 'TLSv1.2'
}

const server = require('https').Server(serverOptions, app);

app.get('/', (req, res) => {
	console.log(req.socket.servername);
	res.send(`<h1>Welcome</h1>`);
});

// Start the Server
server.listen(PORT, () => {
	console.log(`[-] Server Listening on Port ${PORT}`);
});
This post is licensed under CC BY 4.0 by the author.