1
0
mirror of https://github.com/golang/go synced 2024-11-17 02:54:45 -07:00

net/http: keep sensitive headers on redirects to the same host

Fixes #35104
This commit is contained in:
Gustavo Falco 2022-08-19 00:14:25 -03:00
parent 9b8750f53e
commit 8d53e71e22
3 changed files with 33 additions and 10 deletions

View File

@ -990,8 +990,8 @@ func shouldCopyHeaderOnRedirect(headerKey string, initial, dest *url.URL) bool {
// directly, we don't know their scope, so we assume // directly, we don't know their scope, so we assume
// it's for *.domain.com. // it's for *.domain.com.
ihost := canonicalAddr(initial) ihost := idnaASCIIFromURL(initial)
dhost := canonicalAddr(dest) dhost := idnaASCIIFromURL(dest)
return isDomainOrSubdomain(dhost, ihost) return isDomainOrSubdomain(dhost, ihost)
} }
// All other headers are copied: // All other headers are copied:

View File

@ -1470,6 +1470,9 @@ func TestClientRedirectResponseWithoutRequest(t *testing.T) {
} }
// Issue 4800: copy (some) headers when Client follows a redirect. // Issue 4800: copy (some) headers when Client follows a redirect.
// Issue 35104: Since both URLs have the same host (localhost)
// but different ports, sensitive headers like Cookie and Authorization
// are preserved.
func TestClientCopyHeadersOnRedirect(t *testing.T) { run(t, testClientCopyHeadersOnRedirect) } func TestClientCopyHeadersOnRedirect(t *testing.T) { run(t, testClientCopyHeadersOnRedirect) }
func testClientCopyHeadersOnRedirect(t *testing.T, mode testMode) { func testClientCopyHeadersOnRedirect(t *testing.T, mode testMode) {
const ( const (
@ -1483,6 +1486,8 @@ func testClientCopyHeadersOnRedirect(t *testing.T, mode testMode) {
"X-Foo": []string{xfoo}, "X-Foo": []string{xfoo},
"Referer": []string{ts2URL}, "Referer": []string{ts2URL},
"Accept-Encoding": []string{"gzip"}, "Accept-Encoding": []string{"gzip"},
"Cookie": []string{"foo=bar"},
"Authorization": []string{"secretpassword"},
} }
if !reflect.DeepEqual(r.Header, want) { if !reflect.DeepEqual(r.Header, want) {
t.Errorf("Request.Header = %#v; want %#v", r.Header, want) t.Errorf("Request.Header = %#v; want %#v", r.Header, want)
@ -1501,9 +1506,11 @@ func testClientCopyHeadersOnRedirect(t *testing.T, mode testMode) {
c := ts1.Client() c := ts1.Client()
c.CheckRedirect = func(r *Request, via []*Request) error { c.CheckRedirect = func(r *Request, via []*Request) error {
want := Header{ want := Header{
"User-Agent": []string{ua}, "User-Agent": []string{ua},
"X-Foo": []string{xfoo}, "X-Foo": []string{xfoo},
"Referer": []string{ts2URL}, "Referer": []string{ts2URL},
"Cookie": []string{"foo=bar"},
"Authorization": []string{"secretpassword"},
} }
if !reflect.DeepEqual(r.Header, want) { if !reflect.DeepEqual(r.Header, want) {
t.Errorf("CheckRedirect Request.Header = %#v; want %#v", r.Header, want) t.Errorf("CheckRedirect Request.Header = %#v; want %#v", r.Header, want)
@ -1707,18 +1714,30 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) {
{"cookie", "http://foo.com/", "http://bar.com/", false}, {"cookie", "http://foo.com/", "http://bar.com/", false},
{"cookie2", "http://foo.com/", "http://bar.com/", false}, {"cookie2", "http://foo.com/", "http://bar.com/", false},
{"authorization", "http://foo.com/", "http://bar.com/", false}, {"authorization", "http://foo.com/", "http://bar.com/", false},
{"authorization", "http://foo.com/", "https://foo.com/", true},
{"authorization", "http://foo.com:1234/", "http://foo.com:4321/", true},
{"www-authenticate", "http://foo.com/", "http://bar.com/", false}, {"www-authenticate", "http://foo.com/", "http://bar.com/", false},
// But subdomains should work: // But subdomains should work:
{"www-authenticate", "http://foo.com/", "http://foo.com/", true}, {"www-authenticate", "http://foo.com/", "http://foo.com/", true},
{"www-authenticate", "http://foo.com/", "http://sub.foo.com/", true}, {"www-authenticate", "http://foo.com/", "http://sub.foo.com/", true},
{"www-authenticate", "http://foo.com/", "http://notfoo.com/", false}, {"www-authenticate", "http://foo.com/", "http://notfoo.com/", false},
{"www-authenticate", "http://foo.com/", "https://foo.com/", false}, {"www-authenticate", "http://foo.com/", "https://foo.com/", true},
{"www-authenticate", "http://foo.com:80/", "http://foo.com/", true}, {"www-authenticate", "http://foo.com:80/", "http://foo.com/", true},
{"www-authenticate", "http://foo.com:80/", "http://sub.foo.com/", true}, {"www-authenticate", "http://foo.com:80/", "http://sub.foo.com/", true},
{"www-authenticate", "http://foo.com:443/", "https://foo.com/", true}, {"www-authenticate", "http://foo.com:443/", "https://foo.com/", true},
{"www-authenticate", "http://foo.com:443/", "https://sub.foo.com/", true}, {"www-authenticate", "http://foo.com:443/", "https://sub.foo.com/", true},
{"www-authenticate", "http://foo.com:1234/", "http://foo.com/", false}, {"www-authenticate", "http://foo.com:1234/", "http://foo.com/", true},
{"authorization", "http://foo.com/", "http://foo.com/", true},
{"authorization", "http://foo.com/", "http://sub.foo.com/", true},
{"authorization", "http://foo.com/", "http://notfoo.com/", false},
{"authorization", "http://foo.com/", "https://foo.com/", true},
{"authorization", "http://foo.com:80/", "http://foo.com/", true},
{"authorization", "http://foo.com:80/", "http://sub.foo.com/", true},
{"authorization", "http://foo.com:443/", "https://foo.com/", true},
{"authorization", "http://foo.com:443/", "https://sub.foo.com/", true},
{"authorization", "http://foo.com:1234/", "http://foo.com/", true},
} }
for i, tt := range tests { for i, tt := range tests {
u0, err := url.Parse(tt.initialURL) u0, err := url.Parse(tt.initialURL)

View File

@ -2743,17 +2743,21 @@ var portMap = map[string]string{
"socks5": "1080", "socks5": "1080",
} }
// canonicalAddr returns url.Host but always with a ":port" suffix. func idnaASCIIFromURL(url *url.URL) string {
func canonicalAddr(url *url.URL) string {
addr := url.Hostname() addr := url.Hostname()
if v, err := idnaASCII(addr); err == nil { if v, err := idnaASCII(addr); err == nil {
addr = v addr = v
} }
return addr
}
// canonicalAddr returns url.Host but always with a ":port" suffix.
func canonicalAddr(url *url.URL) string {
port := url.Port() port := url.Port()
if port == "" { if port == "" {
port = portMap[url.Scheme] port = portMap[url.Scheme]
} }
return net.JoinHostPort(addr, port) return net.JoinHostPort(idnaASCIIFromURL(url), port)
} }
// bodyEOFSignal is used by the HTTP/1 transport when reading response // bodyEOFSignal is used by the HTTP/1 transport when reading response