├── 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 | ssltest.certsimple.com 6 | 7 | 8 | 9 | 10 |

Testing page

11 |

woo.

12 | 13 | 14 | -------------------------------------------------------------------------------- /views/error.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Error 6 | 7 | 8 |

{{ message }}

9 | {{ 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 @mikemaccana 36 | -------------------------------------------------------------------------------- /views/mixpanel.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Using mixpanel 6 | 10 | 13 | 14 | 15 | 16 |

Mixpanel is on

17 |

Chrome will say Your connection to ssltest.certsimple.com is encrypted with outdated cryptography..

18 |

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 | --------------------------------------------------------------------------------