From b981b8ad7a3783f8a6fb2eeee15ee0d2e9abdd4e Mon Sep 17 00:00:00 2001 From: Adam K Dean Date: Tue, 22 Aug 2017 17:30:13 +0100 Subject: [PATCH 01/11] feat: add csrf protection --- dadi/lib/controller/index.js | 1 + dadi/lib/index.js | 9 ++++++++- package.json | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dadi/lib/controller/index.js b/dadi/lib/controller/index.js index 6f769ec0..44d6eb33 100755 --- a/dadi/lib/controller/index.js +++ b/dadi/lib/controller/index.js @@ -161,6 +161,7 @@ Controller.prototype.buildInitialViewData = function (req) { data.global = config.has('global') ? config.get('global') : {} // global values from config data.debug = config.get('debug') data.json = json || false + data.csrfToken = req.csrfToken() delete data.query.json delete data.params.json diff --git a/dadi/lib/index.js b/dadi/lib/index.js index c19712b8..090c6dc5 100755 --- a/dadi/lib/index.js +++ b/dadi/lib/index.js @@ -17,6 +17,8 @@ var raven = require('raven') var serveFavicon = require('serve-favicon') var serveStatic = require('serve-static') var session = require('express-session') +var csrf = require('csurf') +var cookieParser = require('cookie-parser') var toobusy = require('toobusy-js') var dadiStatus = require('@dadi/status') @@ -251,6 +253,10 @@ Server.prototype.start = function (done) { // add the session middleware app.use(session(sessionOptions)) + app.use(csrf()) + } else { + app.use(cookieParser()) + app.use(csrf({ cookie: true })) } // set up cache @@ -467,7 +473,8 @@ Server.prototype.loadApi = function (options, reload, callback) { package: '@dadi/web', version: version, healthCheck: { - baseUrl: 'http://' + + baseUrl: + 'http://' + config.get('server.host') + ':' + config.get('server.port'), diff --git a/package.json b/package.json index 48fe2aeb..5e6239cb 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,8 @@ "connect-redis": "3.2.0", "console-stamp": "^0.2.2", "convict": "3.0.0", + "cookie-parser": "^1.4.3", + "csurf": "^1.9.0", "debug": "^2.6.1", "deepmerge": "^1.3.2", "dustjs-helpers": "^1.7.3", From 586733299d33317eed8895f1c97f145e61598b69 Mon Sep 17 00:00:00 2001 From: Adam K Dean Date: Tue, 22 Aug 2017 17:37:34 +0100 Subject: [PATCH 02/11] feat: add csrf field to security configuration --- config.js | 66 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/config.js b/config.js index d0c48e4d..44e35d5d 100644 --- a/config.js +++ b/config.js @@ -70,7 +70,8 @@ var conf = convict({ env: 'SSL_INTERMEDIATE_CERTIFICATE_PATH' }, sslIntermediateCertificatePaths: { - doc: 'The filenames of SSL intermediate certificates, overrides sslIntermediateCertificate (singular)', + doc: + 'The filenames of SSL intermediate certificates, overrides sslIntermediateCertificate (singular)', format: Array, default: [], env: 'SSL_INTERMEDIATE_CERTIFICATE_PATHS' @@ -201,7 +202,8 @@ var conf = convict({ }, redis: { enabled: { - doc: 'If enabled, cache files will be saved to the specified Redis server', + doc: + 'If enabled, cache files will be saved to the specified Redis server', format: Boolean, default: false }, @@ -232,12 +234,14 @@ var conf = convict({ }, headers: { useGzipCompression: { - doc: "If true, uses gzip compression and adds a 'Content-Encoding:gzip' header to the response.", + doc: + "If true, uses gzip compression and adds a 'Content-Encoding:gzip' header to the response.", format: Boolean, default: true }, cacheControl: { - doc: "A set of custom cache control headers for different content types. For example 'cacheControl': { 'text/css': 'public, max-age=1000' }", + doc: + "A set of custom cache control headers for different content types. For example 'cacheControl': { 'text/css': 'public, max-age=1000' }", format: Object, default: { 'text/css': 'public, max-age=86400', @@ -274,7 +278,8 @@ var conf = convict({ }, accessLog: { enabled: { - doc: "If true, HTTP access logging is enabled. The log file name is similar to the setting used for normal logging, with the addition of 'access'. For example `web.access.log`.", + doc: + "If true, HTTP access logging is enabled. The log file name is similar to the setting used for normal logging, with the addition of 'access'. For example `web.access.log`.", format: Boolean, default: true }, @@ -286,7 +291,8 @@ var conf = convict({ }, sentry: { dsn: { - doc: "The 'DSN' to use for logging errors and events to a Sentry server. It should be similar to 'https://693ef18da3184cffa82144fde2979cbc:a0651b0286784761a62ef8e8fc128722@app.getsentry.com/59524'.", + doc: + "The 'DSN' to use for logging errors and events to a Sentry server. It should be similar to 'https://693ef18da3184cffa82144fde2979cbc:a0651b0286784761a62ef8e8fc128722@app.getsentry.com/59524'.", format: String, default: '' } @@ -328,29 +334,34 @@ var conf = convict({ default: 'dadiweb.sid' }, secret: { - doc: 'This is the secret used to sign the session ID cookie. This can be either a string for a single secret, or an array of multiple secrets. If an array of secrets is provided, only the first element will be used to sign the session ID cookie, while all the elements will be considered when verifying the signature in requests.', + doc: + 'This is the secret used to sign the session ID cookie. This can be either a string for a single secret, or an array of multiple secrets. If an array of secrets is provided, only the first element will be used to sign the session ID cookie, while all the elements will be considered when verifying the signature in requests.', format: String, default: 'dadiwebsecretsquirrel', env: 'SESSION_SECRET' }, resave: { - doc: 'Forces the session to be saved back to the session store, even if the session was never modified during the request.', + doc: + 'Forces the session to be saved back to the session store, even if the session was never modified during the request.', format: Boolean, default: false }, saveUninitialized: { - doc: "Forces a session that is 'uninitialized' to be saved to the store. A session is uninitialized when it is new but not modified.", + doc: + "Forces a session that is 'uninitialized' to be saved to the store. A session is uninitialized when it is new but not modified.", format: Boolean, default: false }, store: { - doc: 'The session store instance, defaults to a new MemoryStore instance.', + doc: + 'The session store instance, defaults to a new MemoryStore instance.', format: String, default: '' }, cookie: { maxAge: { - doc: "Set the cookie’s expiration as an interval of seconds in the future, relative to the time the browser received the cookie. Null means no 'expires' parameter is set so the cookie becomes a browser-session cookie. When the user closes the browser the cookie (and session) will be removed.", + doc: + "Set the cookie’s expiration as an interval of seconds in the future, relative to the time the browser received the cookie. Null means no 'expires' parameter is set so the cookie becomes a browser-session cookie. When the user closes the browser the cookie (and session) will be removed.", format: '*', default: 60000 }, @@ -393,7 +404,8 @@ var conf = convict({ default: false }, stripIndexPages: { - doc: "A set of filenames to remove from URLs. For example ['index.php', 'default.aspx']", + doc: + "A set of filenames to remove from URLs. For example ['index.php', 'default.aspx']", format: Array, default: [] }, @@ -405,12 +417,20 @@ var conf = convict({ }, security: { trustProxy: { - doc: 'If true, trusts the values specified in X-Forwarded-* headers, such as protocol and client IP address', + doc: + 'If true, trusts the values specified in X-Forwarded-* headers, such as protocol and client IP address', format: '*', default: true }, transportSecurity: { - doc: 'If true, requires requests to be secure. Overridden if server.protocol is set to https.', + doc: + 'If true, requires requests to be secure. Overridden if server.protocol is set to https.', + format: '*', + default: false + }, + csrf: { + doc: + 'If true, a CSRF token will be provided, and all form submissions must include this as _csrf', format: '*', default: false } @@ -428,23 +448,27 @@ var conf = convict({ default: {} }, virtualDirectories: { - doc: 'Allows specifying folders where additional static content may reside. An array entry should like look { "path": "data/legacy_features", "index": "default.html", "forceTrailingSlash": false }', + doc: + 'Allows specifying folders where additional static content may reside. An array entry should like look { "path": "data/legacy_features", "index": "default.html", "forceTrailingSlash": false }', format: Array, default: [] }, allowJsonView: { - doc: 'If true, allows appending ?json=true to the querystring to view the raw JSON output for each page.', + doc: + 'If true, allows appending ?json=true to the querystring to view the raw JSON output for each page.', format: Boolean, default: false }, toobusy: { enabled: { - doc: 'If true, server will respond with HTTP 503 if the server is deemed too busy.', + doc: + 'If true, server will respond with HTTP 503 if the server is deemed too busy.', format: Boolean, default: false }, maxLag: { - doc: "The maximum amount of time in milliseconds that the event queue is behind before we consider the process 'too busy'.", + doc: + "The maximum amount of time in milliseconds that the event queue is behind before we consider the process 'too busy'.", format: Number, default: 70 }, @@ -455,12 +479,14 @@ var conf = convict({ } }, cluster: { - doc: 'If true, Web runs in cluster mode, starting a worker for each CPU core', + doc: + 'If true, Web runs in cluster mode, starting a worker for each CPU core', format: Boolean, default: true }, debug: { - doc: 'If true, debug mode is enabled and a panel containing the JSON loaded for each page is displayed alongside the normal content.', + doc: + 'If true, debug mode is enabled and a panel containing the JSON loaded for each page is displayed alongside the normal content.', format: Boolean, default: false }, From d5e9a175cf47a169b751bbdfbf7954237fef68d3 Mon Sep 17 00:00:00 2001 From: Adam K Dean Date: Tue, 22 Aug 2017 18:34:28 +0100 Subject: [PATCH 03/11] feat: only use csrf if enabled in config --- dadi/lib/controller/index.js | 5 ++++- dadi/lib/index.js | 14 ++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/dadi/lib/controller/index.js b/dadi/lib/controller/index.js index 44d6eb33..29b5b332 100755 --- a/dadi/lib/controller/index.js +++ b/dadi/lib/controller/index.js @@ -161,7 +161,10 @@ Controller.prototype.buildInitialViewData = function (req) { data.global = config.has('global') ? config.get('global') : {} // global values from config data.debug = config.get('debug') data.json = json || false - data.csrfToken = req.csrfToken() + + if (config.get('security.csrf')) { + data.csrfToken = req.csrfToken() + } delete data.query.json delete data.params.json diff --git a/dadi/lib/index.js b/dadi/lib/index.js index 090c6dc5..17e66212 100755 --- a/dadi/lib/index.js +++ b/dadi/lib/index.js @@ -253,10 +253,16 @@ Server.prototype.start = function (done) { // add the session middleware app.use(session(sessionOptions)) - app.use(csrf()) - } else { - app.use(cookieParser()) - app.use(csrf({ cookie: true })) + } + + // use csrf protection if enabled + if (config.get('security.csrf')) { + if (sessionConfig.enabled) { + app.use(csrf()) + } else { + app.use(cookieParser()) + app.use(csrf({ cookie: true })) + } } // set up cache From cc437e8438c9d2a1901167b9794cae248e7980d3 Mon Sep 17 00:00:00 2001 From: Adam K Dean Date: Wed, 23 Aug 2017 10:54:05 +0100 Subject: [PATCH 04/11] chore: set sinon version to 2.x.x from latest --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e6239cb..693d5410 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "nodeunit": ">=0.5.1", "prettier": "^1.1.0", "should": "latest", - "sinon": "latest", + "sinon": "^2.x.x", "sinon-test": "^1.0.2", "snazzy": "^6.0.0", "standard": "8.6.0", From 38402a01c6252cd563de225abe4a62185a49b295 Mon Sep 17 00:00:00 2001 From: James Lambie Date: Fri, 25 Aug 2017 10:19:26 +0100 Subject: [PATCH 05/11] test: add tests for CSRF tokens --- test/unit/controller.js | 142 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/test/unit/controller.js b/test/unit/controller.js index 1303968f..ab2683e7 100644 --- a/test/unit/controller.js +++ b/test/unit/controller.js @@ -110,6 +110,148 @@ describe("Controller", function(done) { }) }) + /* + @jimlambie my understanding of csurf's implementation is that if it's used (csrf enabled in web config), then any POST requests must provide a valid token under the name _csrf, and if it either isn't present or is incorrect, then a 403 is generated, which currently gets picked up by web for the custom error page. If you could knock up a couple of tests for this, that'd be great! + + Also, probably worth testing that if csrf is enabled, that the csrfToken is present on the view model. + */ + describe("CSRF", function() { + it("should add a csrfToken to the view context", function(done) { + TestHelper.disableApiConfig().then(() => { + TestHelper.updateConfig({ + security: { + csrf: true + } + }).then(() => { + var pages = TestHelper.setUpPages() + pages[0].datasources = [] + + TestHelper.startServer(pages).then(() => { + var client = request(connectionString) + client + .get(pages[0].routes[0].path + "?json=true") + .expect(200) + .end((err, res) => { + if (err) return done(err) + should.exist(res.body.csrfToken) + done() + }) + }) + }) + }) + }) + + it("should return a 403 if a POST request is missing a csrf token", function( + done + ) { + TestHelper.disableApiConfig().then(() => { + TestHelper.updateConfig({ + security: { + csrf: true + } + }).then(() => { + var pages = TestHelper.setUpPages() + pages[0].datasources = ["categories"] + + TestHelper.startServer(pages).then(() => { + // provide API response + var results = { + categories: { results: [{ _id: 1, title: "books" }] } + } + + sinon + .stub(Controller.Controller.prototype, "loadData") + .yields(null, results) + + var client = request(connectionString) + client + .post(pages[0].routes[0].path + "?json=true") + .send() + .expect(403) + .end((err, res) => { + if (err) return done(err) + Controller.Controller.prototype.loadData.restore() + res.text.indexOf("invalid csrf token").should.be.above(0) + done() + }) + }) + }) + }) + }) + + it("should return 403 when POST request contains an invalid csrfToken", function( + done + ) { + TestHelper.disableApiConfig().then(() => { + TestHelper.updateConfig({ + security: { + csrf: true + } + }).then(() => { + var pages = TestHelper.setUpPages() + pages[0].datasources = [] + + TestHelper.startServer(pages).then(() => { + var client = request(connectionString) + client + .get(pages[0].routes[0].path + "?json=true") + .expect(200) + .end((err, res) => { + if (err) return done(err) + should.exist(res.body.csrfToken) + + client + .post(pages[0].routes[0].path) + .send({ _csrf: "XXX" }) + .expect(403) + .end((err, res) => { + if (err) return done(err) + res.text.indexOf("invalid csrf token").should.be.above(0) + done() + }) + }) + }) + }) + }) + }) + + it("should return 200 when POST request contains a valid csrfToken", function( + done + ) { + TestHelper.disableApiConfig().then(() => { + TestHelper.updateConfig({ + security: { + csrf: true + } + }).then(() => { + var pages = TestHelper.setUpPages() + pages[0].datasources = [] + + TestHelper.startServer(pages).then(() => { + var client = request(connectionString) + client + .get(pages[0].routes[0].path + "?json=true") + .expect(200) + .end((err, res) => { + if (err) return done(err) + should.exist(res.body.csrfToken) + + client + .post(pages[0].routes[0].path) + .send({ _csrf: res.body.csrfToken.toString() }) + .expect(200) + .end((err, res) => { + if (err) return done(err) + res.text.indexOf("invalid csrf token").should.be.above(0) + done() + }) + }) + }) + }) + }) + }) + }) + describe("Events", function(done) { it("should load events in the order they are specified", function(done) { TestHelper.disableApiConfig().then(() => { From 774b3f7bb30dac133a197ed25c7df397b1cd1b7f Mon Sep 17 00:00:00 2001 From: Adam K Dean Date: Tue, 29 Aug 2017 15:25:41 +0100 Subject: [PATCH 06/11] test: add cookie support to CSRF tests --- test/help.js | 10 ++++++++++ test/unit/controller.js | 43 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/test/help.js b/test/help.js index f0eea79a..8b5cfba6 100755 --- a/test/help.js +++ b/test/help.js @@ -110,6 +110,16 @@ TestHelper.prototype.resetConfig = function() { }) } +TestHelper.prototype.extractCookieValue = function(res, cookieName) { + var cookies = res.headers["set-cookie"] + var cookie = cookies.find(function(cookie) { + return cookie.startsWith(cookieName + "=") + }) + var data = cookie.split(";")[0] + var value = data.split("=")[1] + return value +} + /** * Tests that the response has the Set-Cookie header equal to "name" */ diff --git a/test/unit/controller.js b/test/unit/controller.js index ab2683e7..6249cf74 100644 --- a/test/unit/controller.js +++ b/test/unit/controller.js @@ -1,6 +1,7 @@ var _ = require("underscore") var nock = require("nock") var request = require("supertest") +// var cookieParser = require("cookie-parser") var should = require("should") var sinon = require("sinon") @@ -111,7 +112,10 @@ describe("Controller", function(done) { }) /* - @jimlambie my understanding of csurf's implementation is that if it's used (csrf enabled in web config), then any POST requests must provide a valid token under the name _csrf, and if it either isn't present or is incorrect, then a 403 is generated, which currently gets picked up by web for the custom error page. If you could knock up a couple of tests for this, that'd be great! + @jimlambie my understanding of csurf's implementation is that if it's used (csrf enabled in web config), + then any POST requests must provide a valid token under the name _csrf, and if it either isn't present + or is incorrect, then a 403 is generated, which currently gets picked up by web for the custom error page. + If you could knock up a couple of tests for this, that'd be great! Also, probably worth testing that if csrf is enabled, that the csrfToken is present on the view model. */ @@ -141,6 +145,31 @@ describe("Controller", function(done) { }) }) + it("should set a cookie with the csrf secret", function(done) { + TestHelper.disableApiConfig().then(() => { + TestHelper.updateConfig({ + security: { + csrf: true + } + }).then(() => { + var pages = TestHelper.setUpPages() + pages[0].datasources = [] + + TestHelper.startServer(pages).then(() => { + var client = request(connectionString) + client + .get(pages[0].routes[0].path + "?json=true") + .expect(200) + .expect(TestHelper.shouldSetCookie("_csrf")) + .end((err, res) => { + if (err) return done(err) + done() + }) + }) + }) + }) + }) + it("should return a 403 if a POST request is missing a csrf token", function( done ) { @@ -196,12 +225,17 @@ describe("Controller", function(done) { client .get(pages[0].routes[0].path + "?json=true") .expect(200) + .expect(TestHelper.shouldSetCookie("_csrf")) .end((err, res) => { if (err) return done(err) should.exist(res.body.csrfToken) client .post(pages[0].routes[0].path) + .set( + "Cookie", + "_csrf=" + TestHelper.extractCookieValue(res, "_csrf") + ) .send({ _csrf: "XXX" }) .expect(403) .end((err, res) => { @@ -232,17 +266,22 @@ describe("Controller", function(done) { client .get(pages[0].routes[0].path + "?json=true") .expect(200) + .expect(TestHelper.shouldSetCookie("_csrf")) .end((err, res) => { if (err) return done(err) should.exist(res.body.csrfToken) client .post(pages[0].routes[0].path) + .set( + "Cookie", + "_csrf=" + TestHelper.extractCookieValue(res, "_csrf") + ) .send({ _csrf: res.body.csrfToken.toString() }) .expect(200) .end((err, res) => { if (err) return done(err) - res.text.indexOf("invalid csrf token").should.be.above(0) + res.text.indexOf("invalid csrf token").should.equal(-1) done() }) }) From b924d23dba64c072caa42e8ea7fa2d46810a3253 Mon Sep 17 00:00:00 2001 From: Adam K Dean Date: Tue, 29 Aug 2017 15:31:18 +0100 Subject: [PATCH 07/11] chore: clean up test code --- test/unit/controller.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/unit/controller.js b/test/unit/controller.js index 6249cf74..7d75b290 100644 --- a/test/unit/controller.js +++ b/test/unit/controller.js @@ -1,7 +1,6 @@ var _ = require("underscore") var nock = require("nock") var request = require("supertest") -// var cookieParser = require("cookie-parser") var should = require("should") var sinon = require("sinon") From 303c945f5f84d08690c68a00515d31ad14b9167c Mon Sep 17 00:00:00 2001 From: Adam K Dean Date: Tue, 29 Aug 2017 17:40:55 +0100 Subject: [PATCH 08/11] test: check for specific session name in cookie header in session test --- test/unit/session.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/test/unit/session.js b/test/unit/session.js index 7268385e..fcff5aa3 100644 --- a/test/unit/session.js +++ b/test/unit/session.js @@ -146,14 +146,18 @@ describe("Session", function(done) { TestHelper.startServer(pages).then(() => { var client = request(connectionString) + var sessionName = config.get("sessions.name") - client - .get(pages[0].routes[0].path) - .expect(TestHelper.shouldNotHaveHeader("Set-Cookie")) - .end(function(err, res) { - if (err) return done(err) - done() - }) + client.get(pages[0].routes[0].path).end(function(err, res) { + if (err) return done(err) + + var cookieHeader = res.headers["set-cookie"] + if (cookieHeader) { + cookieHeader.indexOf(sessionName).should.equal(-1) + } + + done() + }) }) }) }) From 21aa824350ea114be2c52ae67803d353af9d8be4 Mon Sep 17 00:00:00 2001 From: James Lambie Date: Wed, 30 Aug 2017 10:07:35 +0100 Subject: [PATCH 09/11] feat: enable csrf security by default --- config.js | 2 +- test/unit/controller.js | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/config.js b/config.js index 44e35d5d..8b33d8c5 100644 --- a/config.js +++ b/config.js @@ -432,7 +432,7 @@ var conf = convict({ doc: 'If true, a CSRF token will be provided, and all form submissions must include this as _csrf', format: '*', - default: false + default: true } }, env: { diff --git a/test/unit/controller.js b/test/unit/controller.js index 7d75b290..d158dc1f 100644 --- a/test/unit/controller.js +++ b/test/unit/controller.js @@ -111,12 +111,9 @@ describe("Controller", function(done) { }) /* - @jimlambie my understanding of csurf's implementation is that if it's used (csrf enabled in web config), - then any POST requests must provide a valid token under the name _csrf, and if it either isn't present - or is incorrect, then a 403 is generated, which currently gets picked up by web for the custom error page. - If you could knock up a couple of tests for this, that'd be great! + If CSRF is used (csrf enabled in web config), then any POST requests must provide a valid token under the name _csrf, and if it either isn't present or is incorrect, then a 403 is generated, which currently gets picked up by web for the custom error page. - Also, probably worth testing that if csrf is enabled, that the csrfToken is present on the view model. + Also, if csrf is enabled, the csrfToken should be present on the view model. */ describe("CSRF", function() { it("should add a csrfToken to the view context", function(done) { From 4e05f9105c328b6688834d59855d2df71f0b1f94 Mon Sep 17 00:00:00 2001 From: David Longworth Date: Wed, 30 Aug 2017 10:41:13 +0100 Subject: [PATCH 10/11] feat: hide error stack when in production mode --- dadi/lib/api/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dadi/lib/api/index.js b/dadi/lib/api/index.js index 1eb79794..cc822da9 100755 --- a/dadi/lib/api/index.js +++ b/dadi/lib/api/index.js @@ -346,7 +346,10 @@ function onError (api) { message: err.message } - data.stack = err.stack ? err.stack : 'Nothing to see' + data.stack = + err.stack && config.get('env') !== 'production' + ? err.stack + : 'Nothing to see' // look for a page that has been loaded // that matches the error code and call its handler if it exists From 59ce4d90296eccbdc0cbc6ed7fc4b0b94da7acf5 Mon Sep 17 00:00:00 2001 From: James Lambie Date: Wed, 30 Aug 2017 10:57:18 +0100 Subject: [PATCH 11/11] chore: revert default to false --- config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.js b/config.js index c880ff8e..8af50fd0 100644 --- a/config.js +++ b/config.js @@ -424,7 +424,7 @@ var conf = convict({ doc: 'If true, a CSRF token will be provided, and all form submissions must include this as _csrf', format: '*', - default: true + default: false } }, env: {