FEATURE: adds support for restricted asset hostname

FEATURE: adds support for restricted asset hostname

Previously __ws param could be used to traverse multisite, this breaking change makes this feature optional

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cafe49b..b616d81 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 2.4.0 - 15-09-2020
+
+ * __ws parameter is only supported for RailsMultisite::ConnectionManagement.asset_hostname
+   previously we would support this for any hostname and careful attackers could use this
+   maliciously. Additionally, if __ws is used we will always strip request cookies as an
+   extra security measure.
+
 ## 2.3.0 - 10-06-2020
 
  * Allow the default connection handler to be changed.
diff --git a/README.md b/README.md
index edfe8fc..643f05b 100644
--- a/README.md
+++ b/README.md
@@ -93,6 +93,19 @@ To get a Rails console that is connected to `some_database_1` database:
 RAILS_DB=db_one rails console
 `‍``
 
+### CDN origin support
+
+To avoid needing to configure many origins you can consider using `RailsMultisite::ConnectionManagement.asset_hostname`
+
+When configured, requests to `asset_hostname`?__ws=another.host.name will be re-routed to the correct site. Cookies will
+be stripped on all incoming requests.
+
+Example:
+
+- Multisite serves `sub.example.com` and `assets.example.com`
+- `RailsMultisite::ConnectionManagement.asset_hostname = 'assets.example.com'`
+- Requests to `https://assets.example.com/route/?__ws=sub.example.com` will be routed to the `sub.example.com`
+
 
 ## Contributing
 
diff --git a/lib/rails_multisite/connection_management.rb b/lib/rails_multisite/connection_management.rb
index 95c8135..c20568d 100644
--- a/lib/rails_multisite/connection_management.rb
+++ b/lib/rails_multisite/connection_management.rb
@@ -35,6 +35,14 @@ module RailsMultisite
       end
     end
 
+    def self.asset_hostname
+      @asset_hostname
+    end
+
+    def self.asset_hostname=(h)
+      @asset_hostname = h
+    end
+
     def self.config_filename
       @instance.config_filename
     end
@@ -370,7 +378,14 @@ module RailsMultisite
       end
 
       request = Rack::Request.new(env)
-      host = request['__ws'] || request.host
+
+      host =
+        if request['__ws'] && request.host == self.class.asset_hostname
+          request.cookies.clear
+          request['__ws']
+        else
+          request.host
+        end
 
       env["RAILS_MULTISITE_HOST"] = host
     end
diff --git a/lib/rails_multisite/version.rb b/lib/rails_multisite/version.rb
index 05a0f61..837c015 100644
--- a/lib/rails_multisite/version.rb
+++ b/lib/rails_multisite/version.rb
@@ -1,5 +1,5 @@
 # frozen_string_literal: true
 #
 module RailsMultisite
-  VERSION = "2.3.0"
+  VERSION = "2.4.0"
 end
diff --git a/spec/middleware_spec.rb b/spec/middleware_spec.rb
index b581d10..da73fd0 100644
--- a/spec/middleware_spec.rb
+++ b/spec/middleware_spec.rb
@@ -17,7 +17,10 @@ describe RailsMultisite::Middleware do
     @app ||= Rack::Builder.new {
       use RailsMultisite::Middleware, config
       map '/html' do
-        run lambda { |env| [200, { 'Content-Type' => 'text/html' }, "<html><BODY><h1>Hi</h1></BODY>\n \t</html>"] }
+        run (proc do |env|
+          request = Rack::Request.new(env)
+          [200, { 'Content-Type' => 'text/html' }, "<html><BODY><h1>#{request.hostname}</h1></BODY>\n \t</html>"]
+        end)
       end
     }.to_app
   end
@@ -26,6 +29,22 @@ describe RailsMultisite::Middleware do
     RailsMultisite::ConnectionManagement.clear_settings!
   end
 
+  describe '__ws lookup support' do
+    it 'returns 200 for valid site' do
+
+      RailsMultisite::ConnectionManagement.asset_hostname = "default.localhost"
+
+      get 'http://second.localhost/html?__ws=default.localhost'
+      expect(last_response).to be_ok
+      expect(last_response).not_to include("second.localhost")
+
+      get 'http://default.localhost/html?__ws=second.localhost'
+      expect(last_response).to be_ok
+      expect(last_response).not_to include("second.hostname")
+    end
+
+  end
+
   describe 'can whitelist a 404 to go to default site' do
 
     let :session do

GitHub sha: 1684290b

1 Like