To enable HSTS (adds Strict-Transport-Security header to the response) for
Node +
Express using
Helmet, this should be sufficient to add to your app.js:
var hsts = require('hsts')
app.use(hsts({
maxAge: 15552000 // 180 days in seconds
}))
But a few things didn't work for me. First of all I had to add the force: true attribute. Also I was using an older version of HSTS ([0.14.0 instead of current latest release 2.0.0), so the maxAge was not in seconds but in milliseconds. It says in the README at github but I hadn't checked the version we were using...
To enable the 'secure' attribute for cookies (so they are only sent over HTTPs), I added the
cookie-session module. But there is an issue with the
secure flag. See the code below for more details.
Below is a full viewcounter.js application that shows the result for enabling hsts and secure cookes. Note also the comments in the code.
Start it with: node viewcounterhelmet.js
viewcounterhelmet.js:
var cookieSession = require('cookie-session')
var helmet = require('helmet');
var express = require('express')
var app = express()
app.use(helmet());
// Works, makes in response show Cache-Control, Pragma, Expires: app.use(helmet.noCache());
var oneYearInSeconds = 31536000;
app.use(helmet.hsts({
maxAge: oneYearInSeconds,
includeSubDomains: true,
force: true
}));
var expiryDate = Date.now() + 60 * 60 * 1000; // 1 hour in milliseconds
app.use(cookieSession({
name: 'session',
secret: '10dfaf09-cf6f-43a9-b40b-4eaacbcceb8a',
maxAge: expiryDate,
// Not setting secure attr does not show it Secure
// secure : true, // Set to true and no cookies at all in response. Created ticket for this for v1.2.0
// secure : false, // Set to false it indeed doesn't show the Secure value with the cookie; it does show the cookies
secureProxy: true, // Since this is running behind a proxy. But deprecated when using 2.0.0-alpha! Says to use secure option but that stops passing on cookies. When set to true, the cookie is set to Secure. If commented out, cookie not set to Secure
httpOnly: true // Don't allow javascript to access the cookie
}))
app.get('/', function (req, res, next) {
// Update something in the session, needed for a cookie to appear
req.session.views = (req.session.views || 0) + 1
// Write response
res.end(req.session.views + ' views')
})
app.listen(3000)
So when issuing the following curl command, this is the result:
vagrant$ curl -c - -v http://localhost:3000/
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.37.1
> Host: localhost:3000
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-Download-Options: noopen
< X-XSS-Protection: 1; mode=block
< Surrogate-Control: no-store
< Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536; includeSubDomains
* Added cookie session="eyJ2aWV3cyI6MX0=" for domain localhost, path /, expire 2961374488
< Set-Cookie: session=eyJ2aWV3cyI6MX0=; path=/; expires=Sun, 04 Nov 2063 04:01:28 GMT; secure; httponly
* Added cookie session.sig="DJaPtrG-tmTnVr33fOWXqWGnVlw" for domain localhost, path /, expire 2961374488
< Set-Cookie: session.sig=DJaPtrG-tmTnVr33fOWXqWGnVlw; path=/; expires=Sun, 04 Nov 2063 04:01:28 GMT; secure; httponly
< Date: Fri, 02 Dec 2016 13:30:45 GMT
< Connection: keep-alive
< Content-Length: 7
<
* Connection #0 to host localhost left intact
1 views# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_localhost FALSE / TRUE 2961374488 session eyJ2aWV3cyI6MX0=
#HttpOnly_localhost FALSE / TRUE 2961374488 session.sig DJaPtrG-tmTnVr33fOWXqWGnVlw
Notice the Strict-Transport-Security header and the set cookies with the 'secure' attribute set.
But when setting secure:true (also for the currently latest version 1.2.0), this is the result:
vagrant$ curl -c - -v http://localhost:3000/
* Hostname was NOT found in DNS cache
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 3000 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.37.1
> Host: localhost:3000
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-Download-Options: noopen
< X-XSS-Protection: 1; mode=block
< Surrogate-Control: no-store
< Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536; includeSubDomains
< Date: Tue, 13 Dec 2016 11:27:37 GMT
< Connection: keep-alive
< Content-Length: 7
<
* Connection #0 to host localhost left intact
So no cookies anymore, while they should be there! Reported issue
here.
Versions used:
- node js: 6.7.0
- express: 4.13.30
- cookie-session: 1.2.0
- helmet: 0.14.0