Skip to content
This repository has been archived by the owner on Feb 2, 2022. It is now read-only.

Commit

Permalink
Improves the security headers
Browse files Browse the repository at this point in the history
Attempts to fix all the security issues reported by the Mozilla
Observatory: https://observatory.mozilla.org/analyze/viz.scrapd.org

Fixes #194
  • Loading branch information
rgreinho committed Jan 24, 2020
1 parent edded2c commit cdb3b7f
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 3 deletions.
54 changes: 54 additions & 0 deletions csp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import helmet from 'helmet';
import uuidv4 from 'uuid/v4';

// Configuration values mostly come from this talk:
// https://pyvideo.org/pybay-2019/browser-security-with-http-headers.html
export default function csp(app) {
// Create a nonce on every request and make it available to other middleware
app.use((req, res, next) => {
res.locals.nonce = Buffer.from(uuidv4()).toString('base64');
next();
});

const nonce = (req, res) => `'nonce-${res.locals.nonce}'`;

const scriptSrc = [nonce, "'strict-dynamic'", "'unsafe-inline'", 'https:'];

// In dev we allow 'unsafe-eval', so HMR doesn't trigger the CSP
if (process.env.NODE_ENV !== 'production') {
scriptSrc.push("'unsafe-eval'");
}

app.use(
helmet({
contentSecurityPolicy: {
directives: {
baseUri: ["'none'"],
objectSrc: ["'none'"],
scriptSrc
}
},
policy: ['strict-origin-when-cross-origin', 'unsafe-url']
})
);

// Sets "Strict-Transport-Security: max-age=31536000"; includeSubDomains; preload.
app.use(
helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
})
);

// Sets "Referrer-Policy: strict-origin-when-cross-origin".
app.use(helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' }));

// The following headers are superseeded by the CSP policy, but still usefull for older browsers.
// Sets "X-Frame-Options: DENY".
app.use(helmet.frameguard({ action: 'deny' }));
// Sets "X-XSS-Protection: 1; mode=block".
app.use(helmet.xssFilter());
// Sets "X-Content-Type-Options: nosniff".
app.use(helmet.noSniff());
}
135 changes: 135 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
"export": "next export",
"fix": "run-s fix:*",
"fix:format": "prettier --write \"pages/**/*.js\" \"components/**/*.js\"",
"fix:javascript": "eslint --ext js,html --fix pages components",
"fix:javascript": "eslint --ext js,html --fix pages components csp.js server.js",
"lint": "run-s lint:*",
"lint:javascript": "eslint --ext js,html pages components",
"lint:javascript": "eslint --ext js,html pages components csp.js server.js",
"ship": "run-s dump && ./tools/deploy.sh",
"start": "NODE_ENV=production node server.js",
"test": "jest",
Expand Down Expand Up @@ -48,6 +48,7 @@
"babel-plugin-import": "^1.13.0",
"express": "^4.17.1",
"facepaint": "^1.2.1",
"helmet": "^3.21.2",
"lighthouse": "^5.6.0",
"lighthouse-ci": "^1.10.0",
"lru-cache": "^5.1.1",
Expand Down
6 changes: 5 additions & 1 deletion server.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { join } = require('path');
const express = require('express');
const next = require('next');
const cache = require('lru-cache'); // for using least-recently-used based caching
const csp = require('./csp');

const PORT = 8000;
const dev = process.env.NODE_ENV !== 'production';
Expand All @@ -10,11 +11,14 @@ const handle = app.getRequestHandler();

const ssrCache = new cache({
max: 20, // not more than 20 results will be cached
maxAge: 1000 * 60 * 5, // 5 mins
maxAge: 1000 * 60 * 5 // 5 mins
});

app.prepare().then(() => {
const server = express();

csp(server);

server.get('/', (req, res) => {
renderAndCache(req, res, '/');
});
Expand Down

0 comments on commit cdb3b7f

Please sign in to comment.