├── ssl └── youll_need_your_own_certificate ├── routes └── index.js ├── views ├── index.handlebars ├── error.handlebars └── mixpanel.handlebars ├── .editorconfig ├── ssltest.service ├── package.json ├── README.md ├── app.js └── bin └── www /ssl/youll_need_your_own_certificate: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var router = express.Router(); 3 | 4 | 5 | router.get('/', function(req, res, next) { 6 | res.render('index', {layout: false}); 7 | }); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /views/index.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |woo.
12 | 13 | 14 | -------------------------------------------------------------------------------- /views/error.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |{{ error.status }}
10 | {{ error.stack }}
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | # Tab indent, JS style
8 | [*]
9 | end_of_line = lf
10 | insert_final_newline = true
11 | indent_size = 2
12 | indent_style = tab
13 |
--------------------------------------------------------------------------------
/ssltest.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=ssltest
3 |
4 | [Service]
5 | ExecStart=/var/www/ssltest/bin/www
6 | Restart=always
7 | User=nobody
8 | Group=nobody
9 | Environment=PATH=/usr/bin:/usr/local/bin
10 | Environment=NODE_ENV=production
11 | WorkingDirectory=/var/www/ssltest
12 |
13 | [Install]
14 | WantedBy=multi-user.target
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ssltest",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "start": "node ./bin/www"
7 | },
8 | "dependencies": {
9 | "body-parser": "~1.12.0",
10 | "cookie-parser": "~1.3.4",
11 | "debug": "~2.1.1",
12 | "express": "~4.12.2",
13 | "express-handlebars": "^2.0.1",
14 | "helmet": "^0.7.0",
15 | "jade": "~1.9.2",
16 | "morgan": "~1.5.1",
17 | "ocsp": "^1.0.1",
18 | "serve-favicon": "~2.2.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A demo Node/Express 4 app that gets A+ on the Qualys SSL Labs SSL Server Test
2 |
3 | This app is fairly boring right now, since the authors patched node itself to implement the correct settings.
4 |
5 | ## Usage
6 |
7 | Requires **node 4.2 +**
8 |
9 | npm install .
10 |
11 | Add your certs to /ssl. The following are expected:
12 |
13 | - Private key `example.com.private-key`
14 | - Certificate `example.crt`
15 | - CA certificate bundle `example-ca.crt`
16 |
17 | Start the server:
18 |
19 | node bin/www
20 |
21 | And visit https://localhost:3000/
22 |
23 | Visit [Qualys SSL Labs SSL Server Test](https://www.ssllabs.com/ssltest)
24 |
25 | Clear the cache for your site if there's an existing entry. This will initiate a new scan.
26 |
27 | As of 2015 10 28 this Express 4 app passed the scan with A+ result.
28 |
29 | ## License
30 |
31 | MIT
32 |
33 | ## Authors
34 |
35 | Mike MacCana Chrome will say Your connection to ssltest.certsimple.com is encrypted with outdated cryptography.
.
Would you like to Turn off mixpanel?
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | var helmet = require('helmet'); 8 | var routes = require('./routes/index'); 9 | var expressHandlebars = require('express-handlebars'); 10 | 11 | var app = express(); 12 | 13 | // view engine setup 14 | app.set('views', path.join(__dirname, 'views')); 15 | app.engine('handlebars', expressHandlebars({})); 16 | app.set('view engine', 'handlebars'); 17 | 18 | var ONE_YEAR = 31536000000; 19 | app.use(helmet.hsts({ 20 | maxAge: ONE_YEAR, 21 | includeSubdomains: true, 22 | force: true 23 | })); 24 | 25 | app.use(logger('dev')); 26 | app.use(bodyParser.json()); 27 | app.use(bodyParser.urlencoded({ extended: false })); 28 | app.use(cookieParser()); 29 | app.use(express.static(path.join(__dirname, 'public'))); 30 | 31 | app.use('/', routes); 32 | 33 | // catch 404 and forward to error handler 34 | app.use(function(req, res, next) { 35 | var err = new Error('Not Found'); 36 | err.status = 404; 37 | next(err); 38 | }); 39 | 40 | // error handlers 41 | 42 | // development error handler 43 | // will print stacktrace 44 | if (app.get('env') === 'development') { 45 | app.use(function(err, req, res, next) { 46 | res.status(err.status || 500); 47 | res.render('error', { 48 | message: err.message, 49 | error: err 50 | }); 51 | }); 52 | } 53 | 54 | // production error handler 55 | // no stacktraces leaked to user 56 | app.use(function(err, req, res, next) { 57 | res.status(err.status || 500); 58 | res.render('error', { 59 | message: err.message, 60 | error: {} 61 | }); 62 | }); 63 | 64 | 65 | module.exports = app; 66 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('../app'); 8 | var debug = require('debug')('ssltest:server'); 9 | var http = require('http'); 10 | var path = require('path'); 11 | var fs = require('fs'); 12 | var https = require('https'); 13 | var ocsp = require('ocsp'); 14 | var log = console.log.bind(console); 15 | 16 | global.appRoot = path.dirname(path.resolve(__dirname)) 17 | 18 | var cache = new ocsp.Cache(); 19 | 20 | // Should be the top level of our express app 21 | var sslDir = global.appRoot+'/ssl/' 22 | var privateKey = fs.readFileSync(sslDir+'example.com.key'), 23 | certificate = fs.readFileSync(sslDir+'example.com.crt'), 24 | certificateAuthority = fs.readFileSync(sslDir+'example-ca.crt'); 25 | 26 | /** 27 | * Get port from environment and store in Express. 28 | */ 29 | 30 | var port = normalizePort(process.env.PORT || '443'); 31 | app.set('port', port); 32 | 33 | log('using port', port) 34 | 35 | /** 36 | * Create HTTPS server. 37 | */ 38 | var server = https.createServer({ 39 | key: privateKey, 40 | cert: certificate, 41 | ca: certificateAuthority 42 | }, app); 43 | 44 | 45 | /** 46 | * Listen on provided port, on all network interfaces. 47 | */ 48 | 49 | server.listen(port); 50 | server.on('error', onError); 51 | server.on('listening', onListening); 52 | 53 | // https://nodejs.org/api/tls.html#tls_event_ocsprequest 54 | // server.on('OCSPRequest', function(cert, issuer, cb) { 55 | // ocsp.getOCSPURI(cert, function(err, uri) { 56 | // if (err) 57 | // return cb(err); 58 | 59 | // var req = ocsp.request.generate(cert, issuer); 60 | // var options = { 61 | // url: uri, 62 | // ocsp: req.data 63 | // }; 64 | 65 | // cache.request(req.id, options, cb); 66 | // }); 67 | // }); 68 | 69 | /** 70 | * Normalize a port into a number, string, or false. 71 | */ 72 | 73 | function normalizePort(val) { 74 | var port = parseInt(val, 10); 75 | 76 | if (isNaN(port)) { 77 | // named pipe 78 | return val; 79 | } 80 | 81 | if (port >= 0) { 82 | // port number 83 | return port; 84 | } 85 | 86 | return false; 87 | } 88 | 89 | /** 90 | * Event listener for HTTP server 'error' event. 91 | */ 92 | 93 | function onError(error) { 94 | if (error.syscall !== 'listen') { 95 | throw error; 96 | } 97 | 98 | var bind = typeof port === 'string' 99 | ? 'Pipe ' + port 100 | : 'Port ' + port; 101 | 102 | 103 | 104 | // handle specific listen errors with friendly messages 105 | switch (error.code) { 106 | case 'EACCES': 107 | console.error(bind + ' requires elevated privileges'); 108 | process.exit(1); 109 | break; 110 | case 'EADDRINUSE': 111 | console.error(bind + ' is already in use'); 112 | process.exit(1); 113 | break; 114 | default: 115 | throw error; 116 | } 117 | } 118 | 119 | /** 120 | * Event listener for HTTP server 'listening' event. 121 | */ 122 | 123 | function onListening() { 124 | var addr = server.address(); 125 | var bind = typeof addr === 'string' 126 | ? 'pipe ' + addr 127 | : 'port ' + addr.port; 128 | debug('Listening on ' + bind); 129 | } 130 | --------------------------------------------------------------------------------