Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content security policy strict mode #29856

Merged
merged 7 commits into from
Feb 4, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/setup/production.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
== Using Kibana in a production environment

* <<configuring-kibana-shield>>
* <<csp-strict-mode>>
* <<enabling-ssl>>
* <<load-balancing>>

Expand Down Expand Up @@ -36,6 +37,25 @@ which users can load which dashboards.
For information about setting up Kibana users, see
{kibana-ref}/using-kibana-with-security.html[Configuring security in Kibana].

[float]
[[csp-strict-mode]]
=== Require Content Security Policy

Kibana uses a Content Security Policy to help prevent the browser from allowing
unsafe scripting, but older browsers will silently ignore this policy. If your
organization does not need to support Internet Explorer 11 or much older
versions of our other supported browsers, we recommend that you enable Kibana's
`strict` mode for content security policy, which will block access to Kibana
for any browser that does not enforce even a rudimentary set of CSP
protections.

To do this, set `csp.strict` to `true` in your `kibana.yml`:

--------
csp.strict: true
--------


[float]
[[enabling-ssl]]
=== Enabling SSL
Expand Down
2 changes: 2 additions & 0 deletions docs/setup/settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ you'll need to update your `kibana.yml` file. You can also enable SSL and set a

`csp.rules:`:: A template https://w3c.github.io/webappsec-csp/[content-security-policy] that disables certain unnecessary and potentially insecure capabilities in the browser. All instances of `{nonce}` will be replaced with an automatically generated nonce at load time. We strongly recommend that you keep the default CSP rules that ship with Kibana.

`csp.strict:`:: *Default: `false`* Blocks access to Kibana to any browser that does not enforce even rudimentary CSP rules. In practice, this will disable support for older, less safe browsers like Internet Explorer.

`elasticsearch.customHeaders:`:: *Default: `{}`* Header names and values to send to Elasticsearch. Any custom headers
cannot be overwritten by client-side headers, regardless of the `elasticsearch.requestHeadersWhitelist` configuration.

Expand Down
1 change: 1 addition & 0 deletions src/server/config/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export default () => Joi.object({

csp: Joi.object({
rules: Joi.array().items(Joi.string()).default(DEFAULT_CSP_RULES),
strict: Joi.boolean().default(false),
}).default(),

cpu: Joi.object({
Expand Down
126 changes: 67 additions & 59 deletions src/ui/ui_render/bootstrap/template.js.hbs
Original file line number Diff line number Diff line change
@@ -1,59 +1,67 @@
window.onload = function () {
var files = [
'{{dllBundlePath}}/vendors.bundle.dll.js',
'{{regularBundlePath}}/commons.bundle.js',
'{{regularBundlePath}}/{{appId}}.bundle.js'
];

var failure = function () {
// make subsequent calls to failure() noop
failure = function () {};

var err = document.createElement('h1');
err.style['color'] = 'white';
err.style['font-family'] = 'monospace';
err.style['text-align'] = 'center';
err.style['background'] = '#F44336';
err.style['padding'] = '25px';
err.innerText = document.querySelector('[data-error-message]').dataset.errorMessage;

document.body.innerHTML = err.outerHTML;
}

function loadStyleSheet(path) {
var dom = document.createElement('link');

dom.addEventListener('error', failure);
dom.setAttribute('rel', 'stylesheet');
dom.setAttribute('href', path);
document.head.appendChild(dom);
}

function createJavascriptElement(path) {
var dom = document.createElement('script');

dom.setAttribute('async', '');
dom.addEventListener('error', failure);
dom.setAttribute('src', file);
dom.addEventListener('load', next);
document.head.appendChild(dom);
}

{{#each styleSheetPaths}}
loadStyleSheet('{{this}}');
{{/each}}

(function next() {
var file = files.shift();
if (!file) return;

var dom = document.createElement('script');

dom.setAttribute('async', '');
dom.setAttribute('nonce', window.__webpack_nonce__);
dom.addEventListener('error', failure);
dom.setAttribute('src', file);
dom.addEventListener('load', next);
document.head.appendChild(dom);
}());
};
if (window.__kbnStrictCsp && window.__kbnCspNotEnforced__) {
var legacyBrowserError = document.getElementById('kbn_legacy_browser_error');
legacyBrowserError.style.display = 'flex';
} else {
var loadingMessage = document.getElementById('kbn_loading_message');
loadingMessage.style.display = 'flex';

window.onload = function () {
var files = [
'{{dllBundlePath}}/vendors.bundle.dll.js',
'{{regularBundlePath}}/commons.bundle.js',
'{{regularBundlePath}}/{{appId}}.bundle.js'
];

var failure = function () {
// make subsequent calls to failure() noop
failure = function () {};

var err = document.createElement('h1');
err.style['color'] = 'white';
err.style['font-family'] = 'monospace';
err.style['text-align'] = 'center';
err.style['background'] = '#F44336';
err.style['padding'] = '25px';
err.innerText = document.querySelector('[data-error-message]').dataset.errorMessage;

document.body.innerHTML = err.outerHTML;
}

function loadStyleSheet(path) {
var dom = document.createElement('link');

dom.addEventListener('error', failure);
dom.setAttribute('rel', 'stylesheet');
dom.setAttribute('href', path);
document.head.appendChild(dom);
}

function createJavascriptElement(path) {
var dom = document.createElement('script');

dom.setAttribute('async', '');
dom.addEventListener('error', failure);
dom.setAttribute('src', file);
dom.addEventListener('load', next);
document.head.appendChild(dom);
}

{{#each styleSheetPaths}}
loadStyleSheet('{{this}}');
{{/each}}

(function next() {
var file = files.shift();
if (!file) return;

var dom = document.createElement('script');

dom.setAttribute('async', '');
dom.setAttribute('nonce', window.__webpack_nonce__);
dom.addEventListener('error', failure);
dom.setAttribute('src', file);
dom.addEventListener('load', next);
document.head.appendChild(dom);
}());
};
}
1 change: 1 addition & 0 deletions src/ui/ui_render/ui_render_mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ export function uiRenderMixin(kbnServer, server, config) {

const response = h.view('ui_app', {
nonce,
strictCsp: config.get('csp.strict'),
uiPublicUrl: `${basePath}/ui`,
bootstrapScriptUrl: `${basePath}/bundles/app/${app.getId()}/bootstrap.js`,
i18n: (id, options) => i18n.translate(id, options),
Expand Down
38 changes: 33 additions & 5 deletions src/ui/ui_render/views/ui_app.pug
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,21 @@ block content
background-color: #{darkMode ? '#242424' : '#F5F7FA'};
}

.kibanaWelcomeTitle {
color: #000;
font-size: 20px;
font-family: Sans-serif;
margin-top: 20px;
animation: fadeIn 1s ease-in-out;
animation-fill-mode: forwards;
opacity: 0;
animation-delay: 1.0s;
}

.kibanaWelcomeText {
color: #222;
font-size: 14px;
font-family: Sans-serif;
margin-top: 20px;
color: #666;
color: #343741;
animation: fadeIn 1s ease-in-out;
animation-fill-mode: forwards;
opacity: 0;
Expand All @@ -35,6 +44,10 @@ block content
margin-top: 40px;
}

.kibanaLoaderWrap + * {
margin-top: 24px;
}

.kibanaLoader {
height: 128px;
width: 128px;
Expand Down Expand Up @@ -101,15 +114,30 @@ block content
}
}


.kibanaWelcomeView
.kibanaWelcomeView(id="kbn_loading_message", style="display: none;")
.kibanaLoaderWrap
.kibanaLoader
.kibanaWelcomeLogoCircle
.kibanaWelcomeLogo
.kibanaWelcomeText(data-error-message=i18n('common.ui.welcomeErrorMessage', { defaultMessage: 'Kibana did not load properly. Check the server output for more information.' }))
| #{i18n('common.ui.welcomeMessage', { defaultMessage: 'Loading Kibana' })}

.kibanaWelcomeView(id="kbn_legacy_browser_error", style="display: none;")
.kibanaLoaderWrap
.kibanaWelcomeLogoCircle
.kibanaWelcomeLogo
h2.kibanaWelcomeTitle
| #{i18n('common.ui.legacyBrowserTitle', { defaultMessage: 'Please upgrade your browser' })}
.kibanaWelcomeText
| #{i18n('common.ui.legacyBrowserMessage', { defaultMessage: 'This Kibana installation has strict security requirements enabled that your current browser does not meet.' })}

script.
// Since this script tag does not contain a nonce, this code will not run
// in browsers that support content security policy(CSP). This is
// intentional as we check for the existence of __kbnCspNotEnforced__ in
// bootstrap.
window.__kbnCspNotEnforced__ = true;
script(nonce=nonce).
window.__kbnStrictCsp = !{strictCsp};
epixa marked this conversation as resolved.
Show resolved Hide resolved
window.__webpack_nonce__ = '!{nonce}';
script(src=bootstrapScriptUrl, nonce=nonce)