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:
parent
9b8750f53e
commit
8d53e71e22
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user