From 1cbf478ab772748f7e31fedbffdfd7334959e9f8 Mon Sep 17 00:00:00 2001 From: Court Ewing Date: Thu, 31 Jan 2019 12:36:24 -0500 Subject: [PATCH] configurable CSP owned by security team --- .github/CODEOWNERS | 1 + src/server/config/schema.js | 6 ++++++ src/ui/ui_render/ui_render_mixin.js | 18 +++++++----------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dc862068c68f54a..f834bfffe1261f6 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,6 +14,7 @@ # Security /x-pack/plugins/security/ @elastic/kibana-security /x-pack/plugins/spaces/ @elastic/kibana-security +/src/server/csp/ @elastic/kibana-security # Design **/*.scss @elastic/kibana-design diff --git a/src/server/config/schema.js b/src/server/config/schema.js index 1ea4f5e1cc817e1..1073156b16dbae9 100644 --- a/src/server/config/schema.js +++ b/src/server/config/schema.js @@ -29,6 +29,7 @@ import { import { getData } from '../path'; +import { DEFAULT_CSP_RULES, DEFAULT_CSP_LEGACY_BROWSER_RULES } from '../csp'; const tilemapSchema = Joi.object({ url: Joi.string(), @@ -94,6 +95,11 @@ export default () => Joi.object({ exclusive: Joi.boolean().default(false) }).default(), + csp: Joi.object({ + rules: Joi.array().items(Joi.string()).default(DEFAULT_CSP_RULES), + legacyBrowserRules: Joi.array().items(Joi.string()).default(DEFAULT_CSP_LEGACY_BROWSER_RULES), + }).default(), + cpu: Joi.object({ cgroup: Joi.object({ path: Joi.object({ diff --git a/src/ui/ui_render/ui_render_mixin.js b/src/ui/ui_render/ui_render_mixin.js index 7325cbe9c16cb96..8e2444ef1a1c314 100644 --- a/src/ui/ui_render/ui_render_mixin.js +++ b/src/ui/ui_render/ui_render_mixin.js @@ -17,8 +17,7 @@ * under the License. */ -import { createHash, randomBytes } from 'crypto'; -import { promisify } from 'util'; +import { createHash } from 'crypto'; import { props, reduce as reduceAsync } from 'bluebird'; import Boom from 'boom'; import { resolve } from 'path'; @@ -27,8 +26,7 @@ import { i18n } from '@kbn/i18n'; import { AppBootstrap } from './bootstrap'; import { mergeVariables } from './lib'; import { fromRoot } from '../../utils'; - -const randomBytesAsync = promisify(randomBytes); +import { generateCSPNonce, createCSPRuleString } from '../../server/csp'; export function uiRenderMixin(kbnServer, server, config) { function replaceInjectedVars(request, injectedVars) { @@ -215,7 +213,7 @@ export function uiRenderMixin(kbnServer, server, config) { injectedVarsOverrides }); - const nonce = (await randomBytesAsync(12)).toString('base64'); + const nonce = await generateCSPNonce(); const response = h.view('ui_app', { nonce, @@ -245,13 +243,11 @@ export function uiRenderMixin(kbnServer, server, config) { }, }); - const csp = [ - `script-src 'unsafe-eval' 'nonce-${nonce}'`, - 'worker-src blob:', - 'child-src blob:', - ]; + const csp = createCSPRuleString(config.get('csp.rules'), nonce); + response.header('content-security-policy', csp); - response.header('content-security-policy', csp.join(';')); + const legacyCsp = createCSPRuleString(config.get('csp.legacyBrowserRules'), nonce); + response.header('x-content-security-policy', legacyCsp); return response; }