Skip to content

Commit

Permalink
(PUP-11188) Do not add auth and cookie header when redirecting
Browse files Browse the repository at this point in the history
When following HTTP redirects the authentication and cookie header is passed to
different hosts, which may leak sensitive information.

Now the authentication and cookie header is not added when redirecting to
different hosts.  The headers are added when redirecting to the same host, or
when adding the new Puppet setting `:location_trusted`.
  • Loading branch information
Dorin-Pleava authored and luchihoratiu committed Oct 13, 2021
1 parent d3a2ea9 commit 9a8d3ef
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 2 deletions.
6 changes: 6 additions & 0 deletions lib/puppet/defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,12 @@ def self.initialize_default_settings!(settings)
:owner => "service",
:group => "service",
:desc => "The directory where catalog previews per node are generated."
},
:location_trusted => {
:default => false,
:type => :boolean,
:desc => "This will allow sending the name + password and the cookie header to all hosts that puppet may redirect to.
This may or may not introduce a security breach if puppet redirects you to a site to which you'll send your authentication info and cookies."
}
)

Expand Down
2 changes: 1 addition & 1 deletion lib/puppet/http/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ def execute_streaming(request, options: {}, &block)

while !done do
connect(request.uri, options: options) do |http|
apply_auth(request, basic_auth)
apply_auth(request, basic_auth) if redirects.zero?

# don't call return within the `request` block
http.request(request) do |nethttp|
Expand Down
5 changes: 5 additions & 0 deletions lib/puppet/http/redirector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ def redirect_to(request, response, redirects)
new_request = request.class.new(url)
new_request.body = request.body
request.each do |header, value|
unless Puppet[:location_trusted]
# skip adding potentially sensitive header to other hosts
next if header.casecmp('Authorization').zero? && request.uri.host.casecmp(location.host) != 0
next if header.casecmp('Cookie').zero? && request.uri.host.casecmp(location.host) != 0
end
new_request[header] = value
end

Expand Down
59 changes: 58 additions & 1 deletion spec/unit/http/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -597,11 +597,68 @@ def redirect_to(status: 302, url:)
expect(response).to be_success
end

it "preserves basic authorization" do
it "does not preserve basic authorization when redirecting to different hosts" do
stub_request(:get, start_url).with(basic_auth: credentials).to_return(redirect_to(url: other_host))
stub_request(:get, other_host).to_return(status: 200)

client.get(start_url, options: {basic_auth: {user: 'user', password: 'pass'}})
expect(a_request(:get, other_host).
with{ |req| !req.headers.key?('Authorization')}).to have_been_made
end

it "does preserve basic authorization when redirecting to the same hosts" do
stub_request(:get, start_url).with(basic_auth: credentials).to_return(redirect_to(url: bar_url))
stub_request(:get, bar_url).with(basic_auth: credentials).to_return(status: 200)

client.get(start_url, options: {basic_auth: {user: 'user', password: 'pass'}})
expect(a_request(:get, bar_url).
with{ |req| req.headers.key?('Authorization')}).to have_been_made
end

it "does not preserve cookie header when redirecting to different hosts" do
headers = { 'Cookie' => 'TEST_COOKIE'}

stub_request(:get, start_url).with(headers: headers).to_return(redirect_to(url: other_host))
stub_request(:get, other_host).to_return(status: 200)

client.get(start_url, headers: headers)
expect(a_request(:get, other_host).
with{ |req| !req.headers.key?('Cookie')}).to have_been_made
end

it "does preserve cookie header when redirecting to the same hosts" do
headers = { 'Cookie' => 'TEST_COOKIE'}

stub_request(:get, start_url).with(headers: headers).to_return(redirect_to(url: bar_url))
stub_request(:get, bar_url).with(headers: headers).to_return(status: 200)

client.get(start_url, headers: headers)
expect(a_request(:get, bar_url).
with{ |req| req.headers.key?('Cookie')}).to have_been_made
end

it "does preserves cookie header and basic authentication when Puppet[:location_trusted] is true redirecting to different hosts" do
headers = { 'cookie' => 'TEST_COOKIE'}
Puppet[:location_trusted] = true

stub_request(:get, start_url).with(headers: headers, basic_auth: credentials).to_return(redirect_to(url: other_host))
stub_request(:get, other_host).with(headers: headers, basic_auth: credentials).to_return(status: 200)

client.get(start_url, headers: headers, options: {basic_auth: {user: 'user', password: 'pass'}})
expect(a_request(:get, other_host).
with{ |req| req.headers.key?('Authorization') && req.headers.key?('Cookie')}).to have_been_made
end

it "treats hosts as case-insensitive" do
start_url = URI("https://www.EXAmple.com:8140/Start")
bar_url = "https://www.example.com:8140/bar"

stub_request(:get, start_url).with(basic_auth: credentials).to_return(redirect_to(url: bar_url))
stub_request(:get, bar_url).with(basic_auth: credentials).to_return(status: 200)

client.get(start_url, options: {basic_auth: {user: 'user', password: 'pass'}})
expect(a_request(:get, bar_url).
with{ |req| req.headers.key?('Authorization')}).to have_been_made
end

it "redirects given a relative location" do
Expand Down

0 comments on commit 9a8d3ef

Please sign in to comment.