From 027e2e8bb4e199640d188bfaf80e0dff4d823130 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 10 Feb 2021 11:38:17 -0800 Subject: [PATCH] Prevent open redirect when allowed host starts with a dot [CVE-2021-22881] Thanks to @tktech (https://hackerone.com/tktech) for reporting this issue and the patch! --- .../middleware/host_authorization.rb | 17 +++++++++++++---- .../test/dispatch/host_authorization_test.rb | 11 +++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/host_authorization.rb b/actionpack/lib/action_dispatch/middleware/host_authorization.rb index 4564bdafe0..291fdee864 100644 --- a/actionpack/lib/action_dispatch/middleware/host_authorization.rb +++ b/actionpack/lib/action_dispatch/middleware/host_authorization.rb @@ -103,11 +103,20 @@ def call(env) private def authorized?(request) - origin_host = request.get_header("HTTP_HOST").to_s.sub(/:\d+\z/, "") - forwarded_host = request.x_forwarded_host.to_s.split(/,\s?/).last.to_s.sub(/:\d+\z/, "") + valid_host = / + \A + (?[a-z0-9.-]+|\[[a-f0-9]*:[a-f0-9\.:]+\]) + (:\d+)? + \z + /x - @permissions.allows?(origin_host) && - (forwarded_host.blank? || @permissions.allows?(forwarded_host)) + origin_host = valid_host.match( + request.get_header("HTTP_HOST").to_s.downcase) + forwarded_host = valid_host.match( + request.x_forwarded_host.to_s.split(/,\s?/).last) + + origin_host && @permissions.allows?(origin_host[:host]) && ( + forwarded_host.nil? || @permissions.allows?(forwarded_host[:host])) end def excluded?(request) diff --git a/actionpack/test/dispatch/host_authorization_test.rb b/actionpack/test/dispatch/host_authorization_test.rb index 79240ab9b1..3cf1410a31 100644 --- a/actionpack/test/dispatch/host_authorization_test.rb +++ b/actionpack/test/dispatch/host_authorization_test.rb @@ -221,6 +221,17 @@ class HostAuthorizationTest < ActionDispatch::IntegrationTest assert_match "Blocked host: www.example.com", response.body end + test "only compare to valid hostnames" do + @app = ActionDispatch::HostAuthorization.new(App, ".example.com") + + get "/", env: { + "HOST" => "example.com#sub.example.com", + } + + assert_response :forbidden + assert_match "Blocked host: example.com#sub.example.com", response.body + end + test "config setting action_dispatch.hosts_response_app is deprecated" do assert_deprecated do ActionDispatch::HostAuthorization.new(App, "example.com", ->(env) { true }) -- 2.26.2