Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActionCable: Same origin match against X-FORWARDED-HOST when proxying #34716

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion actioncable/lib/action_cable/connection/base.rb
Expand Up @@ -200,7 +200,8 @@ def allow_request_origin?
return true if server.config.disable_request_forgery_protection

proto = Rack::Request.new(env).ssl? ? "https" : "http"
if server.config.allow_same_origin_as_host && env["HTTP_ORIGIN"] == "#{proto}://#{env['HTTP_HOST']}"
host = env["HTTP_X_FORWARDED_HOST"].blank? ? env["HTTP_HOST"] : env["HTTP_X_FORWARDED_HOST"].split(/,\s*/).first
if server.config.allow_same_origin_as_host && env["HTTP_ORIGIN"] == "#{proto}://#{host}"
true
elsif Array(server.config.allowed_request_origins).any? { |allowed_origin| allowed_origin === env["HTTP_ORIGIN"] }
true
Expand Down
26 changes: 18 additions & 8 deletions actioncable/test/connection/cross_site_forgery_test.rb
Expand Up @@ -64,29 +64,39 @@ def send_async(method, *args)
assert_origin_not_allowed "http://rails.co.uk"
end

test "allow same origin as the first host in X-FORWARDED-HOST" do
@server.config.allow_same_origin_as_host = true
@server.config.allowed_request_origins = []
assert_origin_allowed "http://#{HOST}", HOST, "foo"
assert_origin_allowed "http://#{HOST}", HOST, "foo", "bar"
assert_origin_not_allowed "http://#{HOST}", "foo", "bar", "baz"
end

private
def assert_origin_allowed(origin)
response = connect_with_origin origin
def assert_origin_allowed(origin, *proxy)
response = connect_with_origin origin, proxy
assert_equal(-1, response[0])
end

def assert_origin_not_allowed(origin)
response = connect_with_origin origin
def assert_origin_not_allowed(origin, *proxy)
response = connect_with_origin origin, proxy
assert_equal 404, response[0]
end

def connect_with_origin(origin)
def connect_with_origin(origin, proxy)
response = nil

run_in_eventmachine do
response = Connection.new(@server, env_for_origin(origin)).process
response = Connection.new(@server, env_for_origin(origin, proxy)).process
end

response
end

def env_for_origin(origin)
def env_for_origin(origin, proxy)
host = proxy.any? ? proxy.last : HOST

Rack::MockRequest.env_for "/test", "HTTP_CONNECTION" => "upgrade", "HTTP_UPGRADE" => "websocket", "SERVER_NAME" => HOST,
"HTTP_HOST" => HOST, "HTTP_ORIGIN" => origin
"HTTP_HOST" => host, "HTTP_ORIGIN" => origin, "HTTP_X_FORWARDED_HOST" => proxy.join(", ")
end
end