Revert ensure external redirects are explicitly allowed

This commit is contained in:
Gannon McGibbon 2019-01-22 11:40:13 -05:00
parent e26f0658da
commit 2e0ca9284a
8 changed files with 21 additions and 79 deletions

View File

@ -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

View File

@ -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)

View File

@ -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 = "<html><body>You are being <a href=\"#{ERB::Util.unwrapped_html_escape(response.location)}\">redirected</a>.</body></html>"
end
@ -88,13 +88,9 @@ module ActionController
# All other options that can be passed to <tt>redirect_to</tt> 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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