From 5e755fff9d70a7fd3c4f85bb524f1b774dd85b25 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Thu, 13 Dec 2018 09:49:45 +0100 Subject: [PATCH] add Content Security Policy (#1252) * add Content Security Policy * remove reflect-metadata on production builds to get rid of unsafe-eval * fix baseCSP usage * add SRI to CSP * add blob: to media-src * remove SRI * CSP set to reportOnly * adding data: to connect-src CSP * remove block-all-mixed-content * add report-uri support --- client/src/environments/environment.ts | 7 +++ client/src/polyfills.ts | 8 +++- config/default.yaml | 2 + config/production.yaml.example | 2 + server.ts | 3 ++ server/controllers/client.ts | 3 +- server/initializers/constants.ts | 1 + server/middlewares/csp.ts | 45 +++++++++++++++++++ server/middlewares/dnt.ts | 2 +- server/middlewares/index.ts | 2 + .../config/custom-environment-variables.yaml | 4 +- 11 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 server/middlewares/csp.ts diff --git a/client/src/environments/environment.ts b/client/src/environments/environment.ts index 5bb6f4b34..1ea483554 100644 --- a/client/src/environments/environment.ts +++ b/client/src/environments/environment.ts @@ -2,6 +2,13 @@ // `ng build --env=prod` then `environment.prod.ts` will be used instead. // The list of which env maps to which file can be found in `.angular-cli.json`. +// Reflect.metadata polyfill is only needed in the JIT/dev mode. +// +// In order to load these polyfills early enough (before app code), polyfill.ts imports this file to +// to change the order in the final bundle. +import 'core-js/es6/reflect' +import 'core-js/es7/reflect' + export const environment = { production: false, hmr: false, diff --git a/client/src/polyfills.ts b/client/src/polyfills.ts index 5dff18632..368908432 100644 --- a/client/src/polyfills.ts +++ b/client/src/polyfills.ts @@ -45,7 +45,13 @@ import 'core-js/es7/object' /** IE10 and IE11 requires the following for the Reflect API. */ // For Google Bot -import 'core-js/es6/reflect' +// import 'core-js/es6/reflect'; // --> dealt with in src/environment.ts + +/** + * Evergreen browsers require these. + */ +// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. +// import 'core-js/es7/reflect' // --> dealt with in src/environment.ts /** * Required to support Web Animations `@angular/platform-browser/animations`. diff --git a/config/default.yaml b/config/default.yaml index 080638a13..5fdb41250 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -163,6 +163,8 @@ instance: "# If you would like to report a security issue\n# you may report it to:\nContact: https://github.com/Chocobozzz/PeerTube/blob/develop/SECURITY.md\nContact: mailto:" services: + # You can provide a reporting endpoint for Content Security Policy violations + csp-logger: # Cards configuration to format video in Twitter twitter: username: '@Chocobozzz' # Indicates the Twitter account for the website or platform on which the content was published diff --git a/config/production.yaml.example b/config/production.yaml.example index 770bb97da..c0dbf64b6 100644 --- a/config/production.yaml.example +++ b/config/production.yaml.example @@ -177,6 +177,8 @@ instance: "# If you would like to report a security issue\n# you may report it to:\nContact: https://github.com/Chocobozzz/PeerTube/blob/develop/SECURITY.md\nContact: mailto:" services: + # You can provide a reporting endpoint for Content Security Policy violations + csp-logger: # Cards configuration to format video in Twitter twitter: username: '@Chocobozzz' # Indicates the Twitter account for the website or platform on which the content was published diff --git a/server.ts b/server.ts index 4a2a6ddf5..6dff16f46 100644 --- a/server.ts +++ b/server.ts @@ -53,6 +53,9 @@ if (errorMessage !== null) { app.set('trust proxy', CONFIG.TRUST_PROXY) // Security middleware +import { baseCSP } from './server/middlewares' + +app.use(baseCSP) app.use(helmet({ frameguard: { action: 'deny' // we only allow it for /videos/embed, see server/controllers/client.ts diff --git a/server/controllers/client.ts b/server/controllers/client.ts index 73b40cf65..e5bd487f1 100644 --- a/server/controllers/client.ts +++ b/server/controllers/client.ts @@ -2,7 +2,7 @@ import * as express from 'express' import { join } from 'path' import { root } from '../helpers/core-utils' import { ACCEPT_HEADERS, STATIC_MAX_AGE } from '../initializers' -import { asyncMiddleware } from '../middlewares' +import { asyncMiddleware, embedCSP } from '../middlewares' import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '../../shared/models/i18n/i18n' import { ClientHtml } from '../lib/client-html' import { logger } from '../helpers/logger' @@ -22,6 +22,7 @@ clientsRouter.use('/videos/watch/:id', clientsRouter.use('' + '/videos/embed', + embedCSP, (req: express.Request, res: express.Response, next: express.NextFunction) => { res.removeHeader('X-Frame-Options') res.sendFile(embedPath) diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index ad61bee73..f1a734f48 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts @@ -290,6 +290,7 @@ const CONFIG = { get SECURITYTXT_CONTACT () { return config.get('admin.email') } }, SERVICES: { + get 'CSP-LOGGER' () { return config.get('services.csp-logger') }, TWITTER: { get USERNAME () { return config.get('services.twitter.username') }, get WHITELISTED () { return config.get('services.twitter.whitelisted') } diff --git a/server/middlewares/csp.ts b/server/middlewares/csp.ts new file mode 100644 index 000000000..a0ed3710b --- /dev/null +++ b/server/middlewares/csp.ts @@ -0,0 +1,45 @@ +import * as helmet from 'helmet' +import { CONFIG } from '../initializers/constants' + +const baseDirectives = Object.assign({}, + { + defaultSrc: ["'none'"], // by default, not specifying default-src = '*' + connectSrc: ['*', 'data:'], + mediaSrc: ["'self'", 'https:', 'blob:'], + fontSrc: ["'self'", 'data:'], + imgSrc: ["'self'", 'data:'], + scriptSrc: ["'self' 'unsafe-inline'"], + styleSrc: ["'self' 'unsafe-inline'"], + // objectSrc: ["'none'"], // only define to allow plugins, else let defaultSrc 'none' block it + formAction: ["'self'"], + frameAncestors: ["'none'"], + baseUri: ["'self'"], + pluginTypes: ["'none'"], + manifestSrc: ["'self'"], + frameSrc: ["'self'"], // instead of deprecated child-src / self because of test-embed + workerSrc: ["'self'"], // instead of deprecated child-src + upgradeInsecureRequests: true + }, + (CONFIG.SERVICES['CSP-LOGGER'] != null) ? { reportUri: CONFIG.SERVICES['CSP-LOGGER'] } : {} +) + +const baseCSP = helmet.contentSecurityPolicy({ + directives: baseDirectives, + browserSniff: false, + reportOnly: true +}) + +const embedCSP = helmet.contentSecurityPolicy({ + directives: Object.assign(baseDirectives, { + frameAncestors: ['*'] + }), + browserSniff: false, // assumes a modern browser, but allows CDN in front + reportOnly: true +}) + +// --------------------------------------------------------------------------- + +export { + baseCSP, + embedCSP +} diff --git a/server/middlewares/dnt.ts b/server/middlewares/dnt.ts index cabad39c6..607def855 100644 --- a/server/middlewares/dnt.ts +++ b/server/middlewares/dnt.ts @@ -10,4 +10,4 @@ const advertiseDoNotTrack = (_, res, next) => { export { advertiseDoNotTrack - } +} diff --git a/server/middlewares/index.ts b/server/middlewares/index.ts index 0cef26953..b758a8586 100644 --- a/server/middlewares/index.ts +++ b/server/middlewares/index.ts @@ -6,3 +6,5 @@ export * from './pagination' export * from './servers' export * from './sort' export * from './user-right' +export * from './dnt' +export * from './csp' diff --git a/support/docker/production/config/custom-environment-variables.yaml b/support/docker/production/config/custom-environment-variables.yaml index cfc30632c..550f1ad80 100644 --- a/support/docker/production/config/custom-environment-variables.yaml +++ b/support/docker/production/config/custom-environment-variables.yaml @@ -101,9 +101,11 @@ transcoding: 1080: __name: "PEERTUBE_TRANSCODING_1080P" __format: "json" - instance: name: "PEERTUBE_INSTANCE_NAME" description: "PEERTUBE_INSTANCE_DESCRIPTION" terms: "PEERTUBE_INSTANCE_TERMS" + +services: + csp-logger: "PEERTUBE_SERVICES_CSPLOGGER"