From 0902e56162e200c4d3f2928274b2e2979c53d0c1 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Thu, 11 Mar 2021 10:38:34 +0000 Subject: [PATCH] FEATURE: Flag to disable DiscourseConnect CSRF protection (#12356) This is not recommended. But if you have other protections in place for CSRF mitigation, you may wish to disable Discourse's implementation. This site setting is not visible in the UI, and must be changed via the console. --- app/models/discourse_single_sign_on.rb | 24 ++++++++++++++++---- config/site_settings.yml | 3 +++ spec/models/discourse_single_sign_on_spec.rb | 17 ++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/app/models/discourse_single_sign_on.rb b/app/models/discourse_single_sign_on.rb index 3daa48dee36..4abfc61a4a9 100644 --- a/app/models/discourse_single_sign_on.rb +++ b/app/models/discourse_single_sign_on.rb @@ -31,21 +31,37 @@ class DiscourseSingleSignOn < SingleSignOn def register_nonce(return_path) if nonce - @secure_session.set(nonce_key, return_path, expires: SingleSignOn.nonce_expiry_time) + if SiteSetting.discourse_connect_csrf_protection + @secure_session.set(nonce_key, return_path, expires: SingleSignOn.nonce_expiry_time) + else + Discourse.cache.write(nonce_key, return_path, expires_in: SingleSignOn.nonce_expiry_time) + end end end def nonce_valid? - nonce && @secure_session[nonce_key].present? + if SiteSetting.discourse_connect_csrf_protection + nonce && @secure_session[nonce_key].present? + else + nonce && Discourse.cache.read(nonce_key).present? + end end def return_path - @secure_session[nonce_key] || "/" + if SiteSetting.discourse_connect_csrf_protection + @secure_session[nonce_key] || "/" + else + Discourse.cache.read(nonce_key) || "/" + end end def expire_nonce! if nonce - @secure_session[nonce_key] = nil + if SiteSetting.discourse_connect_csrf_protection + @secure_session[nonce_key] = nil + else + Discourse.cache.delete nonce_key + end end end diff --git a/config/site_settings.yml b/config/site_settings.yml index 300bb24fdec..a2baeccf36e 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -475,6 +475,9 @@ login: discourse_connect_overrides_website: false discourse_connect_overrides_card_background: false discourse_connect_not_approved_url: "" + discourse_connect_csrf_protection: + default: true + hidden: true blocked_email_domains: default: "mailinator.com" type: list diff --git a/spec/models/discourse_single_sign_on_spec.rb b/spec/models/discourse_single_sign_on_spec.rb index edf3b856abc..e1d03c12e73 100644 --- a/spec/models/discourse_single_sign_on_spec.rb +++ b/spec/models/discourse_single_sign_on_spec.rb @@ -375,10 +375,27 @@ describe DiscourseSingleSignOn do sso = DiscourseSingleSignOn.parse(payload, secure_session: secure_session) expect(sso.nonce_valid?).to eq true + other_session_sso = DiscourseSingleSignOn.parse(payload, secure_session: SecureSession.new("differentsession")) + expect(other_session_sso.nonce_valid?).to eq false + sso.expire_nonce! expect(sso.nonce_valid?).to eq false + end + it "allows disabling CSRF protection" do + SiteSetting.discourse_connect_csrf_protection = false + _ , payload = DiscourseSingleSignOn.generate_url(secure_session: secure_session).split("?") + + sso = DiscourseSingleSignOn.parse(payload, secure_session: secure_session) + expect(sso.nonce_valid?).to eq true + + other_session_sso = DiscourseSingleSignOn.parse(payload, secure_session: SecureSession.new("differentsession")) + expect(other_session_sso.nonce_valid?).to eq true + + sso.expire_nonce! + + expect(sso.nonce_valid?).to eq false end it "generates a correct sso url" do