FEATURE: Allow embedding to ignore HTTP REFERER

FEATURE: Allow embedding to ignore HTTP REFERER

New site setting: embed_any_origin that will send postMessages to wildcard origins * instead of the referer.

Most of the time you won’t want to do this, so the setting is default to false. However, there are certain situations where you want to allow embedding to send post messages when there is no HTTP REFERER.

For example, if you created a native mobile app and you wanted to embed a list of Discourse topics as HTML. In the code your HTML would be a static file/string, which would not be able to send a referer. In this case, the site setting will allow the embed to work.

From a security standpoint we currently only use postMessage to send data about the size of the HTML document and scroll position, so it should be enable if required with minimal security ramifications.

diff --git a/app/controllers/embed_controller.rb b/app/controllers/embed_controller.rb
index c4bde73..5490fa5 100644
--- a/app/controllers/embed_controller.rb
+++ b/app/controllers/embed_controller.rb
@@ -8,7 +8,7 @@ class EmbedController < ApplicationController
   skip_before_action :check_xhr, :preload_json, :verify_authenticity_token
 
   before_action :ensure_embeddable, except: [ :info, :topics ]
-  before_action :get_embeddable_css_class, except: [ :info, :topics ]
+  before_action :prepare_embeddable, except: [ :info ]
   before_action :ensure_api_request, only: [ :info ]
 
   layout 'embed'
@@ -123,10 +123,13 @@ class EmbedController < ApplicationController
 
   private
 
-  def get_embeddable_css_class
+  def prepare_embeddable
     @embeddable_css_class = ""
     embeddable_host = EmbeddableHost.record_for_url(request.referer)
     @embeddable_css_class = " class=\"#{embeddable_host.class_name}\"" if embeddable_host.present? && embeddable_host.class_name.present?
+
+    @data_referer = request.referer
+    @data_referer = '*' if SiteSetting.embed_any_origin? && @data_referer.blank?
   end
 
   def ensure_api_request
diff --git a/app/views/layouts/embed.html.erb b/app/views/layouts/embed.html.erb
index d1cf2be..b7b714f 100644
--- a/app/views/layouts/embed.html.erb
+++ b/app/views/layouts/embed.html.erb
@@ -13,7 +13,7 @@
       <title><%= @topic_view.page_title %> - <%= SiteSetting.title %></title>
     <%- end %>
 
-    <meta id="data-embedded" data-referer="<%= request.referer %>">
+    <meta id="data-embedded" data-referer="<%= @data_referer %>">
     <%= preload_script 'embed-application' %>
 
     <%= yield :head %>
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index ff522b0..9f3b705 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -1959,6 +1959,7 @@ en:
     autohighlight_all_code: "Force apply code highlighting to all preformatted code blocks even when they didn't explicitly specify the language."
     highlighted_languages: "Included syntax highlighting rules. (Warning: including too many languages may impact performance) see: <a href='https://highlightjs.org/static/demo/' target='_blank'>https://highlightjs.org/static/demo</a> for a demo"
 
+    embed_any_origin: "Allow embeddable content regardless of origin. This is required for mobile apps with static HTML."
     embed_topics_list: "Support HTML embedding of topics lists"
     embed_truncate: "Truncate the embedded posts."
     embed_support_markdown: "Support Markdown formatting for embedded posts."
@@ -4598,4 +4599,4 @@ en:
     html_missing_placeholder: "The html template must include %{placeholder}"
 
   discord:
-    not_in_allowed_guild: 'Authentication failed. You are not a member of a permitted Discord guild.'
\ No newline at end of file
+    not_in_allowed_guild: 'Authentication failed. You are not a member of a permitted Discord guild.'
diff --git a/config/site_settings.yml b/config/site_settings.yml
index c1a6cbf..9a5327f 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -859,6 +859,7 @@ posting:
     choices:
       - 4-spaces-indent
       - code-fences
+  embed_any_origin: false
   embed_topics_list: false
   embed_truncate:
     default: true
diff --git a/spec/requests/embed_controller_spec.rb b/spec/requests/embed_controller_spec.rb
index 8d945dc..5f6abb6 100644
--- a/spec/requests/embed_controller_spec.rb
+++ b/spec/requests/embed_controller_spec.rb
@@ -88,12 +88,29 @@ describe EmbedController do
 
       it "returns a list of topics" do
         topic = Fabricate(:topic)
-        get '/embed/topics?discourse_embed_id=de-1234', headers: headers
+        get '/embed/topics?discourse_embed_id=de-1234', headers: {
+          'REFERER' => 'https://example.com/evil-trout'
+        }
         expect(response.status).to eq(200)
         expect(response.headers['X-Frame-Options']).to eq("ALLOWALL")
         expect(response.body).to match("data-embed-id=\"de-1234\"")
         expect(response.body).to match("data-topic-id=\"#{topic.id}\"")
+        expect(response.body).to match("data-referer=\"https://example.com/evil-trout\"")
       end
+
+      it "returns no referer if not supplied" do
+        get '/embed/topics?discourse_embed_id=de-1234'
+        expect(response.status).to eq(200)
+        expect(response.body).to match("data-referer=\"\"")
+      end
+
+      it "returns * for the referer if `embed_any_origin` is set" do
+        SiteSetting.embed_any_origin = true
+        get '/embed/topics?discourse_embed_id=de-1234'
+        expect(response.status).to eq(200)
+        expect(response.body).to match("data-referer=\"\\*\"")
+      end
+
     end
   end

GitHub sha: 1cebe767

1 Like