diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 1a1b1034aa..a6344bdb7c 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -11,12 +11,6 @@ *Rafael Mendonça França* -* Ensure external redirects are explicitly allowed - - Add `fallback_location` and `allow_other_host` options to `redirect_to`. - - *Gannon McGibbon* - * Introduce ActionDispatch::HostAuthorization This is a new middleware that guards against DNS rebinding attacks by diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index 205f84ae36..93fd57b640 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -13,7 +13,7 @@ module ActionController ACTION_OPTIONS = [:only, :except, :if, :unless] URL_OPTIONS = [:protocol, :host, :domain, :subdomain, :port, :path] - REDIRECT_OPTIONS = [:status, :flash, :alert, :notice, :allow_other_host] + REDIRECT_OPTIONS = [:status, :flash, :alert, :notice] module ClassMethods # :nodoc: def force_ssl(options = {}) @@ -41,7 +41,6 @@ module ActionController host: request.host, path: request.fullpath, status: :moved_permanently, - allow_other_host: true, } if host_or_options.is_a?(Hash) diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 8bd003f5ed..67c198d150 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -60,7 +60,7 @@ module ActionController raise AbstractController::DoubleRenderError if response_body self.status = _extract_redirect_to_status(options, response_options) - self.location = _compute_safe_redirect_to_location(request, options, response_options) + self.location = _compute_redirect_to_location(request, options) self.response_body = "You are being redirected." end @@ -88,13 +88,9 @@ module ActionController # All other options that can be passed to redirect_to are accepted as # options and the behavior is identical. def redirect_back(fallback_location:, allow_other_host: true, **args) - referer = request.headers.fetch("Referer", fallback_location) - response_options = { - fallback_location: fallback_location, - allow_other_host: allow_other_host, - **args, - } - redirect_to referer, response_options + referer = request.headers["Referer"] + redirect_to_referer = referer && (allow_other_host || _url_host_allowed?(referer)) + redirect_to redirect_to_referer ? referer : fallback_location, **args end def _compute_redirect_to_location(request, options) #:nodoc: @@ -118,23 +114,6 @@ module ActionController public :_compute_redirect_to_location private - def _compute_safe_redirect_to_location(request, options, response_options) - location = _compute_redirect_to_location(request, options) - location_options = options.is_a?(Hash) ? options : {} - if response_options[:allow_other_host] || _url_host_allowed?(location, location_options) - location - else - fallback_location = response_options.fetch(:fallback_location) do - raise ArgumentError, <<~MSG.squish - Unsafe redirect #{location.inspect}, - use :fallback_location to specify a fallback - or :allow_other_host to redirect anyway. - MSG - end - _compute_redirect_to_location(request, fallback_location) - end - end - def _extract_redirect_to_status(options, response_options) if options.is_a?(Hash) && options.key?(:status) Rack::Utils.status_code(options.delete(:status)) @@ -145,8 +124,8 @@ module ActionController end end - def _url_host_allowed?(url, options = {}) - URI(url.to_s).host.in?([request.host, options[:host]]) + def _url_host_allowed?(url) + URI(url.to_s).host == request.host rescue ArgumentError, URI::Error false end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index c7aae034dd..ecb8c37e6b 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -28,13 +28,13 @@ class ActionPackAssertionsController < ActionController::Base def redirect_to_path() redirect_to "/some/path" end - def redirect_invalid_external_route() redirect_to "ht_tp://www.rubyonrails.org", allow_other_host: true end + def redirect_invalid_external_route() redirect_to "ht_tp://www.rubyonrails.org" end def redirect_to_named_route() redirect_to route_one_url end - def redirect_external() redirect_to "http://www.rubyonrails.org", allow_other_host: true; end + def redirect_external() redirect_to "http://www.rubyonrails.org"; end - def redirect_external_protocol_relative() redirect_to "//www.rubyonrails.org", allow_other_host: true; end + def redirect_external_protocol_relative() redirect_to "//www.rubyonrails.org"; end def response404() head "404 AWOL" end diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index cbebc6b59c..0562c16284 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -25,11 +25,11 @@ module Another end def redirector - redirect_to "http://foo.bar/", allow_other_host: true + redirect_to "http://foo.bar/" end def filterable_redirector - redirect_to "http://secret.foo.bar/", allow_other_host: true + redirect_to "http://secret.foo.bar/" end def data_sender diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 945d2275c0..998498e1b2 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -49,11 +49,11 @@ class RedirectController < ActionController::Base end def url_redirect_with_status - redirect_to("http://www.example.com", status: :moved_permanently, allow_other_host: true) + redirect_to("http://www.example.com", status: :moved_permanently) end def url_redirect_with_status_hash - redirect_to("http://www.example.com", status: 301, allow_other_host: true) + redirect_to("http://www.example.com", status: 301) end def relative_url_redirect_with_status @@ -81,27 +81,19 @@ class RedirectController < ActionController::Base end def redirect_to_url - redirect_to "http://www.rubyonrails.org/", allow_other_host: true - end - - def redirect_to_unsafe_url redirect_to "http://www.rubyonrails.org/" end - def redirect_to_relative_unsafe_url - redirect_to ".br" - end - def redirect_to_url_with_unescaped_query_string - redirect_to "http://example.com/query?status=new", allow_other_host: true + redirect_to "http://example.com/query?status=new" end def redirect_to_url_with_complex_scheme - redirect_to "x-test+scheme.complex:redirect", allow_other_host: true + redirect_to "x-test+scheme.complex:redirect" end def redirect_to_url_with_network_path_reference - redirect_to "//www.rubyonrails.org/", allow_other_host: true + redirect_to "//www.rubyonrails.org/" end def redirect_to_existing_record @@ -121,12 +113,12 @@ class RedirectController < ActionController::Base end def redirect_to_with_block - redirect_to proc { "http://www.rubyonrails.org/" }, allow_other_host: true + redirect_to proc { "http://www.rubyonrails.org/" } end def redirect_to_with_block_and_assigns @url = "http://www.rubyonrails.org/" - redirect_to proc { @url }, allow_other_host: true + redirect_to proc { @url } end def redirect_to_with_block_and_options @@ -253,28 +245,6 @@ class RedirectTest < ActionController::TestCase assert_redirected_to "http://www.rubyonrails.org/" end - def test_redirect_to_unsafe_url - error = assert_raises(ArgumentError) do - get :redirect_to_unsafe_url - end - assert_equal <<~MSG.squish, error.message - Unsafe redirect \"http://www.rubyonrails.org/\", - use :fallback_location to specify a fallback or - :allow_other_host to redirect anyway. - MSG - end - - def test_redirect_to_relative_unsafe_url - error = assert_raises(ArgumentError) do - get :redirect_to_relative_unsafe_url - end - assert_equal <<~MSG.squish, error.message - Unsafe redirect \"http://test.host.br\", - use :fallback_location to specify a fallback or - :allow_other_host to redirect anyway. - MSG - end - def test_redirect_to_url_with_unescaped_query_string get :redirect_to_url_with_unescaped_query_string assert_response :redirect diff --git a/activestorage/app/controllers/active_storage/blobs_controller.rb b/activestorage/app/controllers/active_storage/blobs_controller.rb index a8e42d7356..4fc3fbe824 100644 --- a/activestorage/app/controllers/active_storage/blobs_controller.rb +++ b/activestorage/app/controllers/active_storage/blobs_controller.rb @@ -9,6 +9,6 @@ class ActiveStorage::BlobsController < ActiveStorage::BaseController def show expires_in ActiveStorage.service_urls_expire_in - redirect_to @blob.service_url(disposition: params[:disposition]), allow_other_host: true + redirect_to @blob.service_url(disposition: params[:disposition]) end end diff --git a/activestorage/app/controllers/active_storage/representations_controller.rb b/activestorage/app/controllers/active_storage/representations_controller.rb index d01af5d939..98e11e5dbb 100644 --- a/activestorage/app/controllers/active_storage/representations_controller.rb +++ b/activestorage/app/controllers/active_storage/representations_controller.rb @@ -9,6 +9,6 @@ class ActiveStorage::RepresentationsController < ActiveStorage::BaseController def show expires_in ActiveStorage.service_urls_expire_in - redirect_to @blob.representation(params[:variation_key]).processed.service_url(disposition: params[:disposition]), allow_other_host: true + redirect_to @blob.representation(params[:variation_key]).processed.service_url(disposition: params[:disposition]) end end