diff --git a/config/routes/admin.js b/config/routes/admin.js index e3299b7..f05d554 100755 --- a/config/routes/admin.js +++ b/config/routes/admin.js @@ -1,11 +1,13 @@ 'use strict' const router = require('express').Router() +const uuid = require('node-uuid') const mw = require('../middleware.js') const debug = require('debug')('tracman-routes-admin') const User = require('../models.js').user router.get('/', mw.ensureAdmin, async (req, res) => { + res.locals.nonce = uuid.v4() try { let found = await User.find({}).sort({lastLogin: -1}) res.render('admin', { diff --git a/package-lock.json b/package-lock.json index f488824..a3e1c0a 100755 --- a/package-lock.json +++ b/package-lock.json @@ -2984,7 +2984,8 @@ }, "jsbn": { "version": "0.1.1", - "bundled": true + "bundled": true, + "optional": true }, "json-schema": { "version": "0.2.3", @@ -5174,6 +5175,11 @@ "tar-pack": "3.4.1" } }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" + }, "nodemailer": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.4.1.tgz", diff --git a/package.json b/package.json index 98cb65b..7720738 100755 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "express": "^4.15.5", "express-request-limit": "^1.0.2", "helmet": "^3.12.0", + "helmet-csp": "^2.7.0", "jquery": "^3.2.1", "load-google-maps-api": "^1.0.0", "minifier": "^0.8.1", @@ -22,6 +23,7 @@ "mongo-sanitize": "^1.0.0", "mongoose": "^4.11.13", "mongoose-unique-validator": "^1.0.6", + "node-uuid": "^1.4.8", "nodemailer": "^4.1.1", "nunjucks": "^3.0.1", "passport": "^0.3.2", diff --git a/server.js b/server.js index a22ede7..e93052e 100755 --- a/server.js +++ b/server.js @@ -3,6 +3,7 @@ /* IMPORTS */ const express = require('express') const helmet = require('helmet') +const csp = require('helmet-csp') const rateLimit = require('express-request-limit') const bodyParser = require('body-parser') const cookieParser = require('cookie-parser') @@ -55,10 +56,12 @@ let ready_promise_list = [] app.set('view engine', 'html') } -/* Express session and settings */ { - app.use(helmet()) - app.use(cookieParser(env.cookie)) - app.use(cookieSession({ +/* Express session and settings */ app.use( + helmet.referrerPolicy({ + policy: 'strict-origin', + }), + cookieParser(env.cookie), + cookieSession({ cookie: { maxAge: 1000 * 60 * 60 * 24 * 7, // 1 week secure: true, @@ -68,18 +71,23 @@ let ready_promise_list = [] secret: env.session, saveUninitialized: true, resave: true, - })) - app.use(bodyParser.json()) - app.use(bodyParser.urlencoded({ + }), + bodyParser.json(), + bodyParser.urlencoded({ extended: true, - })) - app.use(flash()) -} + }), + flash() +) + +/* Report CSP violations */ +app.post('/csp-violation', (req, res) => { + console.log(`CSP Violation! \n${JSON.stringify(req.body)}`) + res.status(204).end() +}) /* Auth */ { require('./config/passport.js')(passport) - app.use(passport.initialize()) - app.use(passport.session()) + app.use(passport.initialize(), passport.session()) } /* Routes */ { @@ -169,10 +177,33 @@ let ready_promise_list = [] } } -// CSRF Protection (keep after routes) -app.use(csurf({ - cookie: true, -})) +// CSRF and CSP Protection (keep after routes) +app.use( + csurf({ + cookie: true, + }), + csp({directives:{ + 'default-src': ["'self'"], + 'script-src': ["'self'", + (req, res) => `'nonce-${res.locals.nonce}'`, + 'https://code.jquery.com', + 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/*', + 'https://www.google.com/recaptcha', + 'https://www.google-analytics.com', + 'https://coin-hive.com', + 'https://coinhive.com', + ], + 'style-src': ["'self'", + 'https://fonts.googleapis.com', + 'https://maxcdn.bootstrapcdn.com', + ], + 'img-src': ["'self'", + 'https://http.cat', + ], + 'object-src': ["'none'"], + 'report-uri': '/csp-violation', + }}) +) /* Sockets */ { sockets.init(io)