Mitigate XSS Attacks with Content Security Policy

I am running 2.6.0 beta 2.

I use the following services in the forums:

  • Google Tag Manager
  • Google Ad Manager
  • Google Ad Sense

Currently, I am using report CSP only while I try to resolve all open issues.

Here are my CSP settings:

With the current settings, I am still receiving LOTS of CSP errors. Some seem like they can be ignored. However this one is boggling me as I have domain declared in the CSP settings.

Am I missing something?

CSP Violation: 'https://www.googletagmanager.com/gtm.js?id=GTM-T9ZW6PR'

backtrace:

/var/www/discourse/app/controllers/csp_reports_controller.rb:9:in `create'
actionpack-6.0.3.2/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
actionpack-6.0.3.2/lib/abstract_controller/base.rb:195:in `process_action'
actionpack-6.0.3.2/lib/action_controller/metal/rendering.rb:30:in `process_action'
actionpack-6.0.3.2/lib/abstract_controller/callbacks.rb:42:in `block in process_action'
activesupport-6.0.3.2/lib/active_support/callbacks.rb:112:in `block in run_callbacks'
/var/www/discourse/app/controllers/application_controller.rb:340:in `block in with_resolved_locale'
i18n-1.8.5/lib/i18n.rb:313:in `with_locale'
/var/www/discourse/app/controllers/application_controller.rb:340:in `with_resolved_locale'
activesupport-6.0.3.2/lib/active_support/callbacks.rb:121:in `block in run_callbacks'
activesupport-6.0.3.2/lib/active_support/callbacks.rb:139:in `run_callbacks'
actionpack-6.0.3.2/lib/abstract_controller/callbacks.rb:41:in `process_action'
actionpack-6.0.3.2/lib/action_controller/metal/rescue.rb:22:in `process_action'
actionpack-6.0.3.2/lib/action_controller/metal/instrumentation.rb:33:in `block in process_action'
activesupport-6.0.3.2/lib/active_support/notifications.rb:180:in `block in instrument'
activesupport-6.0.3.2/lib/active_support/notifications/instrumenter.rb:24:in `instrument'
activesupport-6.0.3.2/lib/active_support/notifications.rb:180:in `instrument'
actionpack-6.0.3.2/lib/action_controller/metal/instrumentation.rb:32:in `process_action'
actionpack-6.0.3.2/lib/action_controller/metal/params_wrapper.rb:245:in `process_action'
activerecord-6.0.3.2/lib/active_record/railties/controller_runtime.rb:27:in `process_action'
actionpack-6.0.3.2/lib/abstract_controller/base.rb:136:in `process'
actionview-6.0.3.2/lib/action_view/rendering.rb:39:in `process'
rack-mini-profiler-2.0.4/lib/mini_profiler/profiling_methods.rb:78:in `block in profile_method'
actionpack-6.0.3.2/lib/action_controller/metal.rb:190:in `dispatch'
actionpack-6.0.3.2/lib/action_controller/metal.rb:254:in `dispatch'
actionpack-6.0.3.2/lib/action_dispatch/routing/route_set.rb:50:in `dispatch'
actionpack-6.0.3.2/lib/action_dispatch/routing/route_set.rb:33:in `serve'
actionpack-6.0.3.2/lib/action_dispatch/journey/router.rb:49:in `block in serve'
actionpack-6.0.3.2/lib/action_dispatch/journey/router.rb:32:in `each'
actionpack-6.0.3.2/lib/action_dispatch/journey/router.rb:32:in `serve'
actionpack-6.0.3.2/lib/action_dispatch/routing/route_set.rb:834:in `call'
/var/www/discourse/lib/middleware/omniauth_bypass_middleware.rb:68:in `call'
rack-2.2.3/lib/rack/tempfile_reaper.rb:15:in `call'
rack-2.2.3/lib/rack/conditional_get.rb:40:in `call'
rack-2.2.3/lib/rack/head.rb:12:in `call'
/var/www/discourse/lib/content_security_policy/middleware.rb:12:in `call'
/var/www/discourse/lib/middleware/anonymous_cache.rb:336:in `call'
rack-2.2.3/lib/rack/session/abstract/id.rb:266:in `context'
rack-2.2.3/lib/rack/session/abstract/id.rb:260:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/cookies.rb:648:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/callbacks.rb:27:in `block in call'
activesupport-6.0.3.2/lib/active_support/callbacks.rb:101:in `run_callbacks'
actionpack-6.0.3.2/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/actionable_exceptions.rb:17:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/debug_exceptions.rb:32:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
logster-2.9.3/lib/logster/middleware/reporter.rb:43:in `call'
railties-6.0.3.2/lib/rails/rack/logger.rb:37:in `call_app'
railties-6.0.3.2/lib/rails/rack/logger.rb:28:in `call'
/var/www/discourse/config/initializers/100-quiet_logger.rb:19:in `call'
/var/www/discourse/config/initializers/100-silence_logger.rb:31:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/request_id.rb:27:in `call'
/var/www/discourse/lib/middleware/enforce_hostname.rb:22:in `call'
rack-2.2.3/lib/rack/method_override.rb:24:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/executor.rb:14:in `call'
rack-2.2.3/lib/rack/sendfile.rb:110:in `call'
actionpack-6.0.3.2/lib/action_dispatch/middleware/host_authorization.rb:76:in `call'
rack-mini-profiler-2.0.4/lib/mini_profiler/profiler.rb:200:in `call'
message_bus-3.3.1/lib/message_bus/rack/middleware.rb:61:in `call'
/var/www/discourse/lib/middleware/request_tracker.rb:176:in `call'
railties-6.0.3.2/lib/rails/engine.rb:527:in `call'
railties-6.0.3.2/lib/rails/railtie.rb:190:in `public_send'
railties-6.0.3.2/lib/rails/railtie.rb:190:in `method_missing'
rack-2.2.3/lib/rack/urlmap.rb:74:in `block in call'
rack-2.2.3/lib/rack/urlmap.rb:58:in `each'
rack-2.2.3/lib/rack/urlmap.rb:58:in `call'
unicorn-5.6.0/lib/unicorn/http_server.rb:632:in `process_client'
unicorn-5.6.0/lib/unicorn/http_server.rb:728:in `worker_loop'
unicorn-5.6.0/lib/unicorn/http_server.rb:548:in `spawn_missing_workers'
unicorn-5.6.0/lib/unicorn/http_server.rb:144:in `start'
unicorn-5.6.0/bin/unicorn:128:in `<top (required)>'
/var/www/discourse/vendor/bundle/ruby/2.6.0/bin/unicorn:23:in `load'
/var/www/discourse/vendor/bundle/ruby/2.6.0/bin/unicorn:23:in `<main>'

Env 1:


hostname	forums-web-only
process_id	27127
application_version	f2e14a3946b020ace5a368614f0da198cd17aa32
HTTP_HOST	forums.paddling.com
REQUEST_URI	/csp_reports
REQUEST_METHOD	POST
HTTP_USER_AGENT	Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:80.0) Gecko/20100101 Firefox/80.0
HTTP_ACCEPT	*/*
HTTP_X_FORWARDED_FOR	74.76.45.218
HTTP_X_REAL_IP	74.76.45.218
time	1:44 pm

Env 2:


hostname	forums-web-only
process_id	27161
application_version	f2e14a3946b020ace5a368614f0da198cd17aa32
HTTP_HOST	forums.paddling.com
REQUEST_URI	/csp_reports
REQUEST_METHOD	POST
HTTP_USER_AGENT	Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0
HTTP_ACCEPT	*/*
HTTP_X_FORWARDED_FOR	66.58.144.146
HTTP_X_REAL_IP	66.58.144.146
time	1:39 pm

Env 3:

hostname	forums-web-only
process_id	27111
application_version	f2e14a3946b020ace5a368614f0da198cd17aa32
HTTP_HOST	forums.paddling.com
REQUEST_URI	/csp_reports
REQUEST_METHOD	POST
HTTP_USER_AGENT	Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
HTTP_ACCEPT	*/*
HTTP_REFERER	https://forums.paddling.com/t/need-advice-loading-a-kayak-onto-j-rac/60058
HTTP_X_FORWARDED_FOR	174.83.24.11
HTTP_X_REAL_IP	174.83.24.11
time	12:16 pm
2 Likes

There are lots and lots of false positives in CSP reporting. See my reply above for more details.

You don’t need most of the rules you have set in your screenshot, just https: and unsafe-inline are enough, they allow all scripts starting with https and all inline scripts. Try cleaning up your CSP sources setting and enabling CSP (without reporting), it should work.

8 Likes

Hi just wondering, the Content-Security-Policy: frame-ancestors ‘none’ directive is one that “will be included in future updates” as mentioned by the OP?

Is it possible to add that directive somehow or should I just wait and not worry about it. Was just doing a hardening exercise and this was the only open item / recommendation by that online security tool. Gives me a lot of confidence in the platform actually, great job guys!

5 Likes

I’m not sure – what do you think @xrav3nz?

4 Likes

I’d say don’t worry about it for now.

frame-ancestors is similar to X-Frame-Options header which Discourse/Rails already enforces. The header is currently set to sameorigin – roughly the same as the CSP directive’s self option.

IMO we won’t gain much from implementing frame-ancestors right now, unless we need to support whitelisting specific domains other than self.

8 Likes

I agree with this.

Chasing every possible vulnerability scanner “issue” can end up breaking things which are important, for a very little risk-benefit ratio.

There are a lot of “vulnerabilities in theory” which are rarely exploited or can only be exploited under a very specific scenario. These types of potential vulnerabilities have never been exploited on a Discourse site, to my knowledge; and I would advise against “fixing” things which are “in theory’” a vulnerability that have not resulted in a breach of any consequence.

That’s my 2 cents as a many decades long cybersecurity professional. I am happy to discuss the basics of cybersecurity risk management if anyone is interested.

5 Likes

We just landed support for CSP frame-ancestors directive. It’s disabled by default for now behind the content security policy frame ancestors site setting. You can add domains to the list using via /admin/customize/embedding as always.

This directive will be enabled by default in the next release cycle.

7 Likes

As promised, this feature was just enabled by default: CSP Frame Ancestors enabled by default

10 Likes

Hello,
I am currently setting up Google Adsense on my website and seems I am missing the “Enable CSP violation report collection at /csp_reports” settings… I am currently self-hosting Discourse on version 2.9.0.beta6

above, in quote, a screenshot of the setting I am missing


above, my setting page for CSP with the “Enable CSP violation report collection at /csp_reports” missing.

Any help is appreciated ! And please tell me if I set-up the settings I have correctly for Adsense, I never used it before :eyes: !

3 Likes

Apologies, my earlier replies here are outdated because as of ~ a month ago we made content_security_policy_collect_reports a hidden setting. You can still enable it but you need to do it via the Rails console, as in:

./launcher enter app
...
rails c 
...
SiteSetting.content_security_policy_collect_reports = true

Note that this is very noisy, I highly recommend not going down this route at all and simply enabling CSP and navigating the site with the browser console open using multiple browsers (Chrome, Firefox, Safari). You’ll find most issues that way. And with the configuration you have, you’re basically allowing almost all that CSP protects against anyway, so you shouldn’t have any need for the reports.

5 Likes

Maybe I am missing something, but I do not see these settings in the settings UI.

  • content_security_policy
  • content_security_policy_report_only
  • content_security_policy_collect_reports (I see that is hidden now)
  • content_security_policy_script_src

Are these options available to hosted instances? I didn’t see any mention of that being a limitation in the original post or comments.

Edit: Also attempted to set the security policy through a theme.

Does not seem to be working as instructed in original post.

image

I’m assuming the hosted plan I’m on doesn’t allow this, even when done via a theme or theme component?

Or maybe I am just doing something totally wrong.

1 Like

Currently, Discourse ships a CSP Level 2 policy with the following directives by default:

  • base-uri restricts the URLs for <base> element

Is there any option to override the default value for the base-uri header?

1 Like

Where are <base> elements actually used in discourse?

Would that break Discourse push notifications?

Could be. Might need a more suitable setting.

1 Like

A post was split to a new topic: What’s the suggested method to use secure images?

Hi there,
May I know how to bypass the issue below? I have added the script invoking a new Function() expression into content_security_policy_script_src, but my browser console still complains below error. Where I can exclude the unsafe-eval? Thanks!

Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive:

1 Like

Apologies. Please ignore my previous post if my below remarks are correct. Just know through above posts I can set ‘unsafe-eval’ directly to content_security_policy_script_src, and there’s no JS error reported in console any more.

1 Like

Maybe have a look at the content security policy script src site setting?

1 Like

Yes. As said in previous replies ‘unsafe-eval’ can be added directly to content_security_policy_script_src, I thought only URL base or full URL allowed to add.

1 Like