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

Implement Content Security Policy (CSP) #1190

Closed
Nutomic opened this issue Oct 3, 2018 · 25 comments · Fixed by #1252
Closed

Implement Content Security Policy (CSP) #1190

Nutomic opened this issue Oct 3, 2018 · 25 comments · Fixed by #1252

Comments

@Nutomic
Copy link
Contributor

Nutomic commented Oct 3, 2018

This would improve the security, and prevent XSS attacks. Here's a good overview:

https://www.html5rocks.com/en/tutorials/security/content-security-policy/

Also check out these links:
https://infosec.mozilla.org/guidelines/web_security#content-security-policy
https://observatory.mozilla.org/analyze.html?host=peertube.social

@rigelk
Copy link
Collaborator

rigelk commented Oct 4, 2018

I already had a try at it some time ago, but it introduced errors at that time so we rolled it back: 4bdd947 😓

@rigelk
Copy link
Collaborator

rigelk commented Oct 4, 2018

I tried https://addons.mozilla.org/en-US/firefox/addon/laboratory-by-mozilla/ and it is really interesting to see how the CSP evolves as you record a session on a PeerTube instance. Kudos to that neat extension.

Unsurprisingly, script-src and style-src are bound to use 'self' 'unsafe-inline' which is far from ideal, but which we can't do without.

What is interesting is the connect-src. Since PeerTube's client loads the video from the origin instance, the CSP should hold all intances's URLs the instance knows about (even generating it per watch page sounds overly complex). That's unrealistic of course, so for now we should use 'connect-src': *.

What do you think about the following?

contentSecurityPolicy: {
  directives: {
    defaultSrc: ['*'],
    mediaSrc: ["'self'"],
    fontSrc: ["'self' data:"],
    imgSrc: ["'self' data:"],
    scriptSrc: ["'self' 'unsafe-inline'"],
    styleSrc: ["'self' 'unsafe-inline'"]
  }
}

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 4, 2018

To be honest I have no idea how CSP works, other than the article I linked above. But it's probably better to have a basic policy than having nothing. And you should also be able to limit child-src and some others.

@rigelk
Copy link
Collaborator

rigelk commented Oct 4, 2018

// Security middleware
app.use(helmet({
  frameguard: {
    action: 'deny' // we only allow it for /videos/embed, see server/controllers/client.ts
  },
  hsts: false,
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ['*'], // by default, not specifying default-src = '*'
      mediaSrc: ["'self'"],
      fontSrc: ["'self' data:"],
      imgSrc: ["'self' data:"],
      scriptSrc: ["'self' 'unsafe-inline'"],
      styleSrc: ["'self' 'unsafe-inline'"],
      objectSrc: ["'none'"],
      pluginTypes: ["'none'"],
      manifestSrc: ["'self'"],
      frameSrc: ["'none'"], // instead of deprecated child-src
      workerSrc: ["'self'"], // instead of deprecated child-src
      upgradeInsecureRequests: true
    },
    browserSniff: false // assumes a modern browser, but allows CDN in front
  },
  referrerPolicy: {
    policy: 'strict-origin-when-cross-origin'
  }
}))

That should be a correct configuration of the CSP (and bonus referrerPolicy to test too) - but would need to be tested on a live instance first.

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 5, 2018

How about merging this for the next release with Content-Security-Policy-Report-Only, and logging the violations somewhere?

@rigelk
Copy link
Collaborator

rigelk commented Oct 5, 2018

I don't think testing through releases is a good process… and we've frozen merges (except bugfixes) until the next release anyway.

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 5, 2018

I could test it on my instance, but I'm not sure how to compile Peertube for development (without breaking anything). And it would still need some way to log the violations on the server.

@rigelk
Copy link
Collaborator

rigelk commented Oct 5, 2018

Please don't test it on your instance, that's too hazardous, especially with an instance that big.

I'll check how to create a reporting endpoint.

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 5, 2018

It should be fine to test with Content-Security-Policy-Report-Only, as that only reports violations, but doesn't enforce the policy (it's in the first link if you haven't read it).

@rigelk
Copy link
Collaborator

rigelk commented Oct 5, 2018

You could use https://gist.github.com/rigelk/54b798bf8098fde431330b3868db133f with https://github.com/mozilla/csp-logger in standalone mode. Since we're only modifying ./server.ts, rebuilding the server can be done with npm run build:server.

@ghost
Copy link

ghost commented Oct 5, 2018

@rigelk you don't need a reporting endpoint.
A browser respecting CSP will log the error in the developer console.

using chrome/chromium, you also will get nice error messages.
The messages in firefox aren't that helpful.

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 7, 2018

@rigelk is that code ignored in debug mode? I'm trying to extract the plain header and set it via Nginx on the server, because that's easier (and imo less risky) than recompiling.

@BO41 that means users would actually have to report the errors.

@rigelk
Copy link
Collaborator

rigelk commented Oct 7, 2018

@Nutomic by debug mode, your mean developing it locally on your computer with npm run dev? The code is not ignored in that case, it is just only applied to the API endpoint: localhost:9000. (it's because we serve the client through angular directly, since it's able to do hot reload and more)

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 7, 2018

@rigelk ah okay, I was only checking localhost:3000. I have it configured now, and it already reported an error:

[2018-10-07 22:15:27.309] [WARN] csp - violation of [script-src] from [default-src *; media-src 'self'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; object-src 'none'; plugin-types 'none'; manifest-src 'self'; frame-src 'none'; worker-src 'self'; upgrade-insecure-requests; report-uri /csp-reporting;]
  on https://peertube.social/video-channels/1a799cd3-7a4a-4b63-83e3-aad1029daa34/videos by eval line:1
  agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
  referrer: https://peertube.social/videos/watch/b757a40a-d1b3-4d8a-b6c8-abd03a3a60f2

@rigelk
Copy link
Collaborator

rigelk commented Oct 7, 2018

Ah right, Angular uses eval, so script-src must be set to 'unsafe-eval'. It kind of defeats the purposes of CSPs for scripts, but that's the best we can do for now.

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 7, 2018

Okay that seems to fix it, no more errors so far after clicking around a bit. I hope there's some way to get rid of the eval usage. Here's a relevant issue, apparently the eval is solved already in Angular:

angular/angular#19142
angular/angular-cli#3430
angular/angular-cli#6872

@rigelk
Copy link
Collaborator

rigelk commented Oct 7, 2018

As said in the last issue you linked, using AOT and removing reflect-metadata allows to get rid of unsafe-eval. Maybe we could remove it in production builds, like suggested in angular/angular-cli#6325 ? ping @Chocobozzz

@rigelk
Copy link
Collaborator

rigelk commented Oct 10, 2018

Meanwhile, I have tested live the CSP configuration I previously suggested and completed it to achieve the following:

  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'none'"], // by default, not specifying default-src = '*'
      connectSrc: ['*'],
      mediaSrc: ["'self'"],
      fontSrc: ["'self' data:"],
      imgSrc: ["'self' data:"],
      scriptSrc: ["'self' 'unsafe-inline' 'unsafe-eval'"],
      styleSrc: ["'self' 'unsafe-inline'"],
      objectSrc: ["'none'"],
      formAction: ["'self'"],
      frameAncestors: ["'*'"],
      baseUri: ["'none'"],
      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
    },
    browserSniff: false // assumes a modern browser, but allows CDN in front
  },

Hopefully we're close to a good first implementation :)

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 10, 2018

@rigelk Can you paste the raw header please?

@rigelk
Copy link
Collaborator

rigelk commented Oct 11, 2018

@Nutomic Content-Security-Policy: default-src 'none'; connect-src *; media-src 'self'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self'; frame-ancestors '*'; base-uri 'none'; plugin-types 'none'; manifest-src 'self'; frame-src 'self'; worker-src 'self'; upgrade-insecure-requests

@Nutomic
Copy link
Contributor Author

Nutomic commented Oct 11, 2018

  • should be frame-ancestors * instead of frame-ancestors '*' (without quotes)
  • should be base-uri 'self' instead of base-uri 'none'
  • Chromium shows the error Invalid plugin type in 'plugin-types' Content Security Policy directive: ''none''. Did you mean to set the object-src directive to 'none'? Apparently this is because object-src should only be set if some plugins are allowed, according to this thread.

Edit: I guess we also need to set media-src *:

[2018-10-11 21:59:07.631] [WARN] csp - violation of [media-src] from [default-src 'none'; connect-src *; media-src 'self'; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self'; frame-ancestors *; base-uri 'self'; ; manifest-src 'self'; frame-src 'self'; worker-src 'self'; upgrade-insecure-requests; report-uri /csp-reporting;]
  on https://peertube.social/videos/watch/7ae45cdb-a2fa-4544-8dfc-77a0b299b58b by blob
  agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.102 Safari/537.36 Vivaldi/2.0.1309.37

Edit2: Weird, it's still reporting the same error with media-src *, but apparently only in Apple browsers:

[2018-10-11 22:33:57.447] [WARN] csp - violation of [media-src] from [default-src 'none'; connect-src *; media-src *; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self'; frame-ancestors *; base-uri 'self'; ; manifest-src 'self'; frame-src 'self'; worker-src 'self'; upgrade-insecure-requests; report-uri /csp-reporting;]
  on https://peertube.social/videos/watch/7ae45cdb-a2fa-4544-8dfc-77a0b299b58b by blob
  agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.102 Safari/537.36 Vivaldi/2.0.1309.37

And another violation for embeds:

[2018-10-11 22:27:53.615] [WARN] csp - violation of [img-src] from [default-src 'none'; connect-src *; media-src *; font-src 'self' data:; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self'; frame-ancestors *; base-uri 'self'; ; manifest-src 'self'; frame-src 'self'; worker-src 'self'; upgrade-insecure-requests; report-uri /csp-reporting;]
  on https://peertube.social/videos/embed/ab8cc45f-8fcf-414e-9492-e5ebfd8e592d by android-webview-video-poster line:1
  agent: Mozilla/5.0 (Linux; Android 8.1.0; Nokia 7 plus Build/OPR1.170623.026; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36

@rigelk
Copy link
Collaborator

rigelk commented Oct 11, 2018

Great! Thanks for the feedback @Nutomic - the plugin-src was confusing as it doesn't yield any error in Chromium :/

Let me try to improve that tomorrow.

@rigelk
Copy link
Collaborator

rigelk commented Oct 12, 2018

With the fixes suggested by @Nutomic, I realised we're using frame-ancestors: frame-ancestors: * and it overrides X-Frame-Options: DENY on browsers that understand this CSP 2 rule.

Right now we're using X-Frame-Options: DENY on all routes except the embed route. We should replicate the behaviour with the CSP.

EDIT: it's a bit complicated, so I suggest we make a first version without frame-ancestors.

@rigelk
Copy link
Collaborator

rigelk commented Oct 12, 2018

Finally I managed to replicate it 🙂

@rigelk
Copy link
Collaborator

rigelk commented Oct 13, 2018

I also managed to remove unsafe-inline as I said in #1190 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants
@Nutomic @Chocobozzz @rigelk and others