What is Content Security Policy?
Content Security Policy (CSP) is an added layer of security that helps to detect and mitigate certain types of attacks, including Cross Site Scripting (XSS) and data injection attacks. These attacks are used for everything from data theft to site defacement to distribution of malware.
– Content Security Policy (CSP) - HTTP | MDN
XSS is still one of the most common web vulnerability – if someone else can run scripts on your site, it is not your site anymore.
Discourse mitigates XSS attacks with CSP, by allowing scripts only from trusted sources to load and execute. Discourse’s default policy employs a strict URL whitelist, and only allows sources that Discourse needs.
Currently, Discourse ships a CSP Level 2 policy with the following directives by default:
script-src
specifies valid sources for JavaScriptsworker-src
specifies valid sources for ServiceWorker scriptsobject-src
blocks the execution of plug-ins (Flash, Java, etc)base-uri
restricts the URLs for<base>
element
The default policy will be expanded in future updates to include more directives.
Available Settings
-
content_security_policy
: Turn on CSP (default on) -
content_security_policy_report_only
: Turn on CSP Report-Only mode (default off)CSP Report Only mode checks and reports the site’s violations, without blocking them. This can be used to experiment with different policies, and monitor their effects.
-
content_security_policy_collect_reports
: Log CSP violations reports in/logs
(default off) -
content_security_policy_script_src
: Extend the defaultscript-src
by defining additional whitelisted script sources
How to Turn on CSP?
Flip the content_security_policy
site setting, and viola, CSP is on!
However, since CSP will start blocking any non-whitelisted script sources immediately, I recommend the following steps to make sure that CSP is not blocking your site’s customizations:
- Turn on CSP Report-Only mode, and check for CSP violations.
- Extend the default CSP as needed
- Turn on Full CSP.
Are My Customizations CSP-Compliant?
1. Inline JavaScripts in Themes / Components
It is very common to have inline JavaScripts in themes:
<script type="text/discourse-plugin" version="0.8">
api.replaceIcon('heart', 'thumbs-up');
</script>
Even though Discourse’s default CSP does not permit them, inline JavaScripts in themes / components are automatically extracted into an external file, so there are no changes required!
2. Inline on
Event Handlers
<a href="#" onclick="doSomething()">Foo</a>
Unfortunately, inline on
event handlers (e.g onclick
, onload
, etc) are also considered as inline scripts. It is recommended to add event listeners via JavaScript.
Here is an example of refactoring the above code:
<a href="#" id="bar">Foo</a>
<script>
// if the element is already in the DOM
document.getElementById('bar').addEventListener('click', doSomething);
// otherwise, try event propagation
$('#main').on('click', '#bar', doSomething);
</script>
3. Tags Linking to External Resources e.g <script>
If your site’s customizations depend on third-party JavaScripts, you may either
- Copy and paste the script content into your theme / component, or
- Extend the default CSP by whitelisting the source.
4. Third-Party Script / Service Integration
Different integrations will have different requirements, but can be addressed similarly.
You could look up the integration’s recommended CSP whitelist, and extend the default CSP accordingly. I suggest simply turn on CSP Report Only mode in Discourse, and watch your console to determine which resources you’ll need to whitelist to make your integrations work.
This is especially important when using third-party script bundlers like Google Tag Manager or Segment, because these bundlers can load many third-party or inline scripts. (For Segment, you might need to add 'unsafe-inline'
to support inline scripts. For GTM, Discourse uses the recommended nonce approach which includes inline script support in GTM custom templates.)
Extending the Default CSP
1. Via Site Setting
Admins can extend their site’s CSP by defining additional script sources to be whitelisted in the content_security_policy_script_src
site setting. They can whitelist scripts individually with the full source path, or if the source is trusted, cover all scripts under the base URL:
You can extend script-src
with all valid source definitions, including hash-sources, and 'unsafe-inline'
if you must.
2. In Plugins
If your plugin integrates or depends on third-party JavaScripts, you may extend the default CSP in plugin.rb
with the extend_content_security_policy
API.
# plugin.rb
extend_content_security_policy(
script_src: ['https://domain.com/script.js', 'https://your-cdn.com/'],
object_src: ['https://domain.com/flash-content']
)
3. In Themes and Components
Define a list
-type theme setting named extend_content_security_policy
:
# settings.yml
extend_content_security_policy:
type: list
default: "script_src: https://script-cdn.com/some/script.js|script_src: https://site1.com"
And this is what it will look like in the UI:
FAQ
I can no longer access my site due to a bad CSP setting, what should I do?
You will need SSH access to your site, and open the Rails console with:
cd /var/discourse
./launcher enter app
rails c
Then,
SiteSetting.content_security_policy_script_src = ""
This will reset your CSP setup back to default, and you should be able to get back to your site.