Experimenting with a 'strict-dynamic' Content Security Policy (CSP)

We’re experimenting with a new ‘strict-dynamic’ CSP, which should make it much easier to integrate themes, plugins and external scripts with Discourse in a secure way.

You can try out the new mode by enabling the ‘content security policy strict dynamic’ site setting. Please let us know if you have any feedback!

We hope to make this the default in the near future, and will make an announcements topic as we get closer to that goal.

For more information, see below:


:scroll: Background

Back in 2018 we introduced a default Content Security Policy for Discourse. At the time, our browser support policy meant we were restricted to using ‘host-source’ values (i.e. listing URL prefixes for every script which should be allowed).

Here in 2024, there are more options available: namely, the 'strict-dynamic' keyword. This removes the need for listing all script URLs in advance. Instead, any initially-trusted scripts (via a ‘hash’ or ‘nonce’ value) are allowed to load more scripts with minimal restrictions.

:eyes: How do I try this new mode?

Enable the ‘content security policy strict dynamic’ setting in your admin panel.

If you don’t see the setting, make sure you’re running the latest version of Discourse. The new feature was merged on 16th February (during the 3.3.0.beta1-dev cycle).

If you’re on our hosting and would like to expedite the update, just let us know!

:thinking: What happens when I enable the new setting?

The Content-Security-Policy script-src directive will be updated to include a single nonce- value, and the 'strict-dynamic' keyword.

All initial <script> tags in core and in themes will be automatically given the appropriate nonce= attribute. No extra work from admins or theme developers is required.

Plugins which inject their own custom <script> tags will need updating to support this new mode (e.g.). We are working to collect data on how widespread this is, and will provide guidance to developers if necessary.

Any host-source values which themes or plugins are contributing will be automatically ignored (browsers do not support host-source alongside strict-dynamic).

:technologist: What about external scripts like GTM, analytics, advertising, etc.?

They should ‘just work’, with no extra configuration required. Let us know if you run into any issues!

:recycle: Is it reversible?

Yup! While we’re running the experiment, you can turn the setting on and off and the change will take effect immediately.

:writing_hand: Example policies

Example Discourse policy before this change:

upgrade-insecure-requests;
base-uri 'self';
object-src 'none';
script-src https://meta.discourse.org/logs/ https://meta.discourse.org/sidekiq/ https://meta.discourse.org/mini-profiler-resources/ https://d11a6trkgmumsb.cloudfront.net/assets/ https://meta.discourse.org/extra-locales/ https://d3bpeqsaub0i6y.cloudfront.net/highlight-js/ https://d3bpeqsaub0i6y.cloudfront.net/javascripts/ https://d3bpeqsaub0i6y.cloudfront.net/plugins/ https://d3bpeqsaub0i6y.cloudfront.net/theme-javascripts/ https://d3bpeqsaub0i6y.cloudfront.net/svg-sprite/ 'sha256-8uAKDaK4QxxCeYZl0Wxad2Nnj2tgKyA14hYBh66pnn0=' 'sha256-QFlnYO2Ll+rgFRKkUmtyRublBc7KFNsbzF7BzoCqjgA=';
worker-src 'self' https://d11a6trkgmumsb.cloudfront.net/assets/ https://d3bpeqsaub0i6y.cloudfront.net/javascripts/ https://d3bpeqsaub0i6y.cloudfront.net/plugins/;
frame-ancestors 'self';
manifest-src 'self';

Example Discourse policy after this change:

upgrade-insecure-requests;
base-uri 'self';
object-src 'none';
script-src 'nonce-78uQrKOCg78iGuMAwCq2lI9E7' 'strict-dynamic';
frame-ancestors 'self';
manifest-src 'self';

:open_book: Resources

MDN strict-dynamic docs

Google’s ‘Strict CSP’ documentation

CSP Evaluator

Caniuse Browser Support Info

22 Likes