Browse Source

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
Rigel Kent 5 years ago
parent
commit
5e755fff9d

+ 7 - 0
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,

+ 7 - 1
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`.

+ 2 - 0
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

+ 2 - 0
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

+ 3 - 0
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

+ 2 - 1
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)

+ 1 - 0
server/initializers/constants.ts

@@ -290,6 +290,7 @@ const CONFIG = {
     get SECURITYTXT_CONTACT () { return config.get<string>('admin.email') }
   },
   SERVICES: {
+    get 'CSP-LOGGER' () { return config.get<string>('services.csp-logger') },
     TWITTER: {
       get USERNAME () { return config.get<string>('services.twitter.username') },
       get WHITELISTED () { return config.get<boolean>('services.twitter.whitelisted') }

+ 45 - 0
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
+}

+ 1 - 1
server/middlewares/dnt.ts

@@ -10,4 +10,4 @@ const advertiseDoNotTrack = (_, res, next) => {
 
 export {
   advertiseDoNotTrack
- }
+}

+ 2 - 0
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'

+ 3 - 1
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"