From ba183ce6543f102ae635502a0da0ac7c923cc97a Mon Sep 17 00:00:00 2001
From: Literallie
Date: Wed, 18 Oct 2017 17:10:23 +0200
Subject: Add basic CSP support
---
app.js | 25 +++++++++++++++++++++++++
lib/config/default.js | 10 ++++++++++
2 files changed, 35 insertions(+)
diff --git a/app.js b/app.js
index 62e6627d..54ec6cf7 100644
--- a/app.js
+++ b/app.js
@@ -108,6 +108,31 @@ if (config.hsts.enable) {
logger.info('https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security')
}
+// use Content-Security-Policy to limit XSS, dangerous plugins, etc.
+// https://helmetjs.github.io/docs/csp/
+if (config.csp.enable) {
+ var cdnDirectives = {
+ scriptSrc: ["https://cdnjs.cloudflare.com"],
+ styleSrc: ["https://cdnjs.cloudflare.com", "https://fonts.googleapis.com"],
+ fontSrc: ["https://cdnjs.cloudflare.com", "https://fonts.gstatic.com"]
+ }
+ var directives = {}
+ for (var propertyName in config.csp.directives) {
+ if(config.csp.directives.hasOwnProperty(propertyName)) {
+ var directive = config.csp.directives[propertyName]
+ if (config.usecdn && !!cdnDirectives[propertyName]) {
+ directive = directive.concat(cdnDirectives[propertyName])
+ }
+ directives[propertyName] = directive;
+ }
+ }
+ app.use(helmet.contentSecurityPolicy({
+ directives: directives
+ }))
+} else {
+ logger.info('Content-Security-Policy is disabled. This may be a security risk.');
+}
+
i18n.configure({
locales: ['en', 'zh', 'fr', 'de', 'ja', 'es', 'ca', 'el', 'pt', 'it', 'tr', 'ru', 'nl', 'hr', 'pl', 'uk', 'hi', 'sv', 'eo', 'da'],
cookie: 'locale',
diff --git a/lib/config/default.js b/lib/config/default.js
index f4c45e3d..e207dfc6 100644
--- a/lib/config/default.js
+++ b/lib/config/default.js
@@ -13,6 +13,16 @@ module.exports = {
includeSubdomains: true,
preload: true
},
+ csp: {
+ enable: true,
+ reportUri: '',
+ directives: {
+ defaultSrc: ["'self'"],
+ scriptSrc: ["'self'"],
+ styleSrc: ["'self'", "'unsafe-inline'"],
+ fontSrc: ["'self'"]
+ }
+ },
protocolusessl: false,
usecdn: true,
allowanonymous: true,
--
cgit v1.2.3
From 5d2d3ec875310de07fe79ae605dfbc0f1df585c5 Mon Sep 17 00:00:00 2001
From: Literallie
Date: Wed, 18 Oct 2017 17:45:57 +0200
Subject: CSP: Upgrade insecure requests if possible
Config option; default is to only upgrade if usessl
---
app.js | 5 +++++
lib/config/default.js | 5 +++--
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/app.js b/app.js
index 54ec6cf7..8af029e7 100644
--- a/app.js
+++ b/app.js
@@ -126,6 +126,11 @@ if (config.csp.enable) {
directives[propertyName] = directive;
}
}
+ if(config.csp.upgradeInsecureRequests === 'auto') {
+ directives.upgradeInsecureRequests = config.usessl === 'true'
+ } else {
+ directives.upgradeInsecureRequests = config.csp.upgradeInsecureRequests === 'true'
+ }
app.use(helmet.contentSecurityPolicy({
directives: directives
}))
diff --git a/lib/config/default.js b/lib/config/default.js
index e207dfc6..217d11d0 100644
--- a/lib/config/default.js
+++ b/lib/config/default.js
@@ -20,8 +20,9 @@ module.exports = {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
- fontSrc: ["'self'"]
- }
+ fontSrc: ["'self'"],
+ },
+ upgradeInsecureRequests: 'auto'
},
protocolusessl: false,
usecdn: true,
--
cgit v1.2.3
From 080436aebb4c4681f85cc8bf5d8563832ff8dbdd Mon Sep 17 00:00:00 2001
From: Literallie
Date: Wed, 18 Oct 2017 17:48:53 +0200
Subject: CSP: Add nonce to slide view inline JS
---
app.js | 7 +++++++
lib/response.js | 3 ++-
package.json | 1 +
public/views/slide.ejs | 2 +-
4 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/app.js b/app.js
index 8af029e7..b78f94e1 100644
--- a/app.js
+++ b/app.js
@@ -12,6 +12,7 @@ var session = require('express-session')
var SequelizeStore = require('connect-session-sequelize')(session.Store)
var fs = require('fs')
var path = require('path')
+var uuid = require('uuid')
var morgan = require('morgan')
var passportSocketIo = require('passport.socketio')
@@ -108,6 +109,11 @@ if (config.hsts.enable) {
logger.info('https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security')
}
+app.use((req, res, next) => {
+ res.locals.nonce = uuid.v4()
+ next()
+})
+
// use Content-Security-Policy to limit XSS, dangerous plugins, etc.
// https://helmetjs.github.io/docs/csp/
if (config.csp.enable) {
@@ -126,6 +132,7 @@ if (config.csp.enable) {
directives[propertyName] = directive;
}
}
+ directives.scriptSrc.push(function (req, res) { return "'nonce-" + res.locals.nonce + "'" })
if(config.csp.upgradeInsecureRequests === 'auto') {
directives.upgradeInsecureRequests = config.usessl === 'true'
} else {
diff --git a/lib/response.js b/lib/response.js
index a22d1e70..287d53e0 100755
--- a/lib/response.js
+++ b/lib/response.js
@@ -584,7 +584,8 @@ function showPublishSlide (req, res, next) {
lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null,
robots: meta.robots || false, // default allow robots
GA: meta.GA,
- disqus: meta.disqus
+ disqus: meta.disqus,
+ cspNonce: res.locals.nonce
}
return renderPublishSlide(data, res)
}).catch(function (err) {
diff --git a/package.json b/package.json
index 4c8dc562..35fe4f9c 100644
--- a/package.json
+++ b/package.json
@@ -114,6 +114,7 @@
"tedious": "^1.14.0",
"to-markdown": "^3.0.3",
"toobusy-js": "^0.5.1",
+ "uuid": "^3.1.0",
"uws": "~0.14.1",
"validator": "^6.2.0",
"velocity-animate": "^1.4.0",
diff --git a/public/views/slide.ejs b/public/views/slide.ejs
index 7ff5016e..c7dd9898 100644
--- a/public/views/slide.ejs
+++ b/public/views/slide.ejs
@@ -41,7 +41,7 @@
-
+
<% if(useCDN) { %>
diff --git a/public/views/pretty.ejs b/public/views/pretty.ejs
index 80d2505c..b2988e39 100644
--- a/public/views/pretty.ejs
+++ b/public/views/pretty.ejs
@@ -72,9 +72,7 @@