diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index fa720358b6b..6eed8c35637 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -947,6 +947,7 @@ en: email_custom_headers: "A pipe-delimited list of custom email headers" email_subject: "Customizable subject format for standard emails. See https://meta.discourse.org/t/customize-subject-format-for-standard-emails/20801" force_https: "Force your site to use HTTPS only. WARNING: do NOT enable this until you verify HTTPS is fully set up and working absolutely everywhere! Did you check your CDN, all social logins, and any external logos / dependencies to make sure they are all HTTPS compatible, too?" + same_site_cookies: "Use same site cookies, they eliminate all vectors Cross Site Request Forgery on supported browsers (Lax or Strict). Warning: Strict will only work on sites that force login and use SSO." summary_score_threshold: "The minimum score required for a post to be included in 'Summarize This Topic'" summary_posts_required: "Minimum posts in a topic before 'Summarize This Topic' is enabled" summary_likes_required: "Minimum likes in a topic before 'Summarize This Topic' is enabled" diff --git a/config/site_settings.yml b/config/site_settings.yml index a0b37e0cb80..171bca9e67d 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -896,6 +896,14 @@ security: force_https: default: false shadowed_by_global: true + same_site_cookies: + default: Lax + type: enum + choices: + - Lax + - Strict + - Disabled + regex: '^(Lax|Strict|Disabled)$' enable_escaped_fragments: true allow_index_in_robots_txt: true enable_noscript_support: true diff --git a/lib/auth/default_current_user_provider.rb b/lib/auth/default_current_user_provider.rb index 4709ac1aebb..0cdfe7ad76a 100644 --- a/lib/auth/default_current_user_provider.rb +++ b/lib/auth/default_current_user_provider.rb @@ -162,12 +162,18 @@ class Auth::DefaultCurrentUserProvider end def cookie_hash(unhashed_auth_token) - { + hash = { value: unhashed_auth_token, httponly: true, expires: SiteSetting.maximum_session_age.hours.from_now, secure: SiteSetting.force_https } + + if SiteSetting.same_site_cookies != "Disabled" + hash[:same_site] = SiteSetting.same_site_cookies + end + + hash end def make_developer_admin(user) diff --git a/lib/discourse_cookie_store.rb b/lib/discourse_cookie_store.rb index 23745a79133..f962ba3a0c9 100644 --- a/lib/discourse_cookie_store.rb +++ b/lib/discourse_cookie_store.rb @@ -10,6 +10,9 @@ class ActionDispatch::Session::DiscourseCookieStore < ActionDispatch::Session::C if SiteSetting.force_https cookie[:secure] = true end + unless SiteSetting.same_site_cookies == "Disabled" + cookie[:same_site] = SiteSetting.same_site_cookies + end end cookie_jar(request)[@key] = cookie end diff --git a/spec/components/auth/default_current_user_provider_spec.rb b/spec/components/auth/default_current_user_provider_spec.rb index 6bfec747383..b33b44641b1 100644 --- a/spec/components/auth/default_current_user_provider_spec.rb +++ b/spec/components/auth/default_current_user_provider_spec.rb @@ -143,10 +143,8 @@ describe Auth::DefaultCurrentUserProvider do token.reload expect(token.auth_token_seen).to eq(false) - freeze_time 21.minutes.from_now - old_token = token.prev_auth_token unverified_token = token.auth_token @@ -222,6 +220,29 @@ describe Auth::DefaultCurrentUserProvider do expect(UserAuthToken.where(user_id: user.id).count).to eq(2) end + it "sets secure, same site lax cookies" do + SiteSetting.force_https = false + SiteSetting.same_site_cookies = "Lax" + + user = Fabricate(:user) + cookies = {} + provider('/').log_on_user(user, {}, cookies) + + + expect(cookies["_t"][:same_site]).to eq("Lax") + expect(cookies["_t"][:httponly]).to eq(true) + expect(cookies["_t"][:secure]).to eq(false) + + SiteSetting.force_https = true + SiteSetting.same_site_cookies = "Disabled" + + cookies = {} + provider('/').log_on_user(user, {}, cookies) + + expect(cookies["_t"][:secure]).to eq(true) + expect(cookies["_t"].key?(:same_site)).to eq(false) + end + it "correctly expires session" do SiteSetting.maximum_session_age = 2 user = Fabricate(:user)