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

Commit 0f8b84d

Browse files
authored
Improves the security headers (#198)
* Improves the security headers Attempts to fix all the security issues reported by the Mozilla Observatory: https://observatory.mozilla.org/analyze/viz.scrapd.org Fixes #194 * Fix bad modules * Fix CI violations * Improves the security headers Attempts to fix all the security issues reported by the Mozilla Observatory: https://observatory.mozilla.org/analyze/viz.scrapd.org Fixes #194 * save * save
1 parent f9bf16e commit 0f8b84d

File tree

4 files changed

+205
-3
lines changed

4 files changed

+205
-3
lines changed

csp.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const helmet = require('helmet');
2+
const uuidv4 = require('uuid/v4');
3+
4+
// Configuration values mostly come from this talk:
5+
// https://pyvideo.org/pybay-2019/browser-security-with-http-headers.html
6+
// export default function csp(app) {
7+
module.exports = function csp(app) {
8+
// Create a nonce on every request and make it available to other middleware
9+
app.use((req, res, next) => {
10+
res.locals.nonce = Buffer.from(uuidv4()).toString('base64');
11+
next();
12+
});
13+
14+
const nonce = (req, res) => `'nonce-${res.locals.nonce}'`;
15+
16+
const scriptSrc = [nonce, 'https:', 'localhost:*'];
17+
18+
// In dev we allow 'unsafe-eval', so HMR doesn't trigger the CSP
19+
if (process.env.NODE_ENV !== 'production') {
20+
scriptSrc.push("'unsafe-eval'");
21+
}
22+
23+
app.use(
24+
helmet({
25+
contentSecurityPolicy: {
26+
directives: {
27+
connectSrc: [
28+
'https://*.tiles.mapbox.com',
29+
'https://api.mapbox.com',
30+
'https://events.mapbox.com',
31+
'https://raw.githubusercontent.com',
32+
'localhost:*'
33+
],
34+
imgSrc: ['data:', 'blob:', 'localhost:*'],
35+
scriptSrc,
36+
workerSrc: ['blob:', 'localhost:*']
37+
}
38+
},
39+
policy: ['strict-origin-when-cross-origin', 'unsafe-url']
40+
})
41+
);
42+
43+
// Sets "Strict-Transport-Security: max-age=31536000"; includeSubDomains; preload.
44+
app.use(
45+
helmet.hsts({
46+
maxAge: 31536000,
47+
includeSubDomains: true,
48+
preload: true
49+
})
50+
);
51+
52+
// Sets "Referrer-Policy: origin.
53+
app.use(helmet.referrerPolicy({ policy: 'origin' }));
54+
55+
// The following headers are superseeded by the CSP policy, but still usefull for older browsers.
56+
// Sets "X-Frame-Options: DENY".
57+
app.use(helmet.frameguard({ action: 'deny' }));
58+
// Sets "X-XSS-Protection: 1; mode=block".
59+
app.use(helmet.xssFilter());
60+
// Sets "X-Content-Type-Options: nosniff".
61+
app.use(helmet.noSniff());
62+
};

package-lock.json

Lines changed: 135 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
"export": "next export",
1212
"fix": "run-s fix:*",
1313
"fix:format": "prettier --write \"pages/**/*.js\" \"components/**/*.js\"",
14-
"fix:javascript": "eslint --ext js,html --fix pages components",
14+
"fix:javascript": "eslint --ext js,html --fix pages components csp.js server.js",
1515
"lint": "run-s lint:*",
16-
"lint:javascript": "eslint --ext js,html pages components",
16+
"lint:javascript": "eslint --ext js,html pages components csp.js server.js",
1717
"ship": "run-s dump && ./tools/deploy.sh",
1818
"start": "NODE_ENV=production node server.js",
1919
"test": "jest",
@@ -48,6 +48,7 @@
4848
"babel-plugin-import": "^1.13.0",
4949
"express": "^4.17.1",
5050
"facepaint": "^1.2.1",
51+
"helmet": "^3.21.2",
5152
"lighthouse": "^5.6.0",
5253
"lighthouse-ci": "^1.10.0",
5354
"lru-cache": "^5.1.1",

server.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const { join } = require('path');
22
const express = require('express');
33
const next = require('next');
44
const cache = require('lru-cache'); // for using least-recently-used based caching
5+
const csp = require('./csp');
56

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

1112
const ssrCache = new cache({
1213
max: 20, // not more than 20 results will be cached
13-
maxAge: 1000 * 60 * 5, // 5 mins
14+
maxAge: 1000 * 60 * 5 // 5 mins
1415
});
1516

1617
app.prepare().then(() => {
1718
const server = express();
19+
20+
csp(server);
21+
1822
server.get('/', (req, res) => {
1923
renderAndCache(req, res, '/');
2024
});

0 commit comments

Comments
 (0)