While moving all inline scripts externally would take a tremendous amount of effort, as far as I can see, adding nonce-source support would be relatively trivial.
nonce-source works by adding a
nonce attribute to any
<script> tags you want to run, which is a base64 string randomly generated on each page load. This nonce is also added to the CSP header, and only scripts with the correct nonce set are allowed to run.
This means inline scripts can be whitelisted without the need to whitelist all inline scripts with
unsafe-inline, and external scripts can be included without the need to whitelist them explicitly in the header (which is useful for themes loading scripts from an external CDN).
To achieve this in Discourse we could generate the nonce on every page load in
ApplicationController, add it to
request.env so we can insert it where we want, and set the appropriate CSP header (or a custom header so CSP headers can be written in the nginx config).
I believe this addresses the problems identified before:
This, along with the other
.html.erb partials containing inline scripts could simply include the
nonce attribute within their script tags.
app/helpers/application_helper#theme_lookup seems to be the method used for inserting themes’
head_tags. It could be extended to parse the html to be inserted, and add the
nonce attribute to any script tags.
This could sit behind a site preference, so sites relying on plugins which don’t update to work with nonces don’t break them.
From the previous CSP feedback, and my own grepping of places
<script> tags appear in the codebase, I can’t see what else would have to be done to make Discourse compatible with nonce-source.
Is there anything I’ve missed? Would a PR be welcome to implement this?
I haven’t yet thought through how this could be tested to ensure
<script> tags lacking a
nonce aren’t added in future.