mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
net/http/cookiejar: treat localhost as secure origin
For development purposes, browsers treat localhost as a secure origin regardless of protocol. Fixes #60997 https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cookies#restrict_access_to_cookies https://bugzilla.mozilla.org/show_bug.cgi?id=1618113 https://issues.chromium.org/issues/40120372 Change-Id: I6d31df4e055f2872c4b93571c53ae5160923852b Reviewed-on: https://go-review.googlesource.com/c/go/+/717860 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Mark Freeman <markfreeman@google.com> Reviewed-by: Damien Neil <dneil@google.com>
This commit is contained in:
parent
f870a1d398
commit
7aa9ca729f
2 changed files with 90 additions and 1 deletions
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/internal/ascii"
|
"net/http/internal/ascii"
|
||||||
|
"net/netip"
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -120,7 +121,7 @@ func (e *entry) id() string {
|
||||||
// request to host/path. It is the caller's responsibility to check if the
|
// request to host/path. It is the caller's responsibility to check if the
|
||||||
// cookie is expired.
|
// cookie is expired.
|
||||||
func (e *entry) shouldSend(https bool, host, path string) bool {
|
func (e *entry) shouldSend(https bool, host, path string) bool {
|
||||||
return e.domainMatch(host) && e.pathMatch(path) && (https || !e.Secure)
|
return e.domainMatch(host) && e.pathMatch(path) && e.secureMatch(https)
|
||||||
}
|
}
|
||||||
|
|
||||||
// domainMatch checks whether e's Domain allows sending e back to host.
|
// domainMatch checks whether e's Domain allows sending e back to host.
|
||||||
|
|
@ -148,6 +149,38 @@ func (e *entry) pathMatch(requestPath string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// secureMatch checks whether a cookie should be sent based on the protocol
|
||||||
|
// and the Secure flag. Localhost is considered a secure origin regardless
|
||||||
|
// of protocol, matching browser behavior.
|
||||||
|
func (e *entry) secureMatch(https bool) bool {
|
||||||
|
if !e.Secure {
|
||||||
|
// Cookies not marked secure are always sent.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Everything below is about cookies marked secure.
|
||||||
|
if https {
|
||||||
|
// HTTPS request matches secure cookies.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Consider localhost to be secure like browsers.
|
||||||
|
if isLocalhost(e.Domain) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ip, err := netip.ParseAddr(e.Domain)
|
||||||
|
if err == nil && ip.IsLoopback() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func isLocalhost(host string) bool {
|
||||||
|
host = strings.TrimSuffix(host, ".")
|
||||||
|
if idx := strings.LastIndex(host, "."); idx >= 0 {
|
||||||
|
host = host[idx+1:]
|
||||||
|
}
|
||||||
|
return ascii.EqualFold(host, "localhost")
|
||||||
|
}
|
||||||
|
|
||||||
// hasDotSuffix reports whether s ends in "."+suffix.
|
// hasDotSuffix reports whether s ends in "."+suffix.
|
||||||
func hasDotSuffix(s, suffix string) bool {
|
func hasDotSuffix(s, suffix string) bool {
|
||||||
return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix
|
return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix
|
||||||
|
|
|
||||||
|
|
@ -471,6 +471,62 @@ var basicsTests = [...]jarTest{
|
||||||
{"https://www.host.test/some/path", "A=a"},
|
{"https://www.host.test/some/path", "A=a"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Secure cookies are sent for localhost",
|
||||||
|
"http://localhost:8910/",
|
||||||
|
[]string{"A=a; secure"},
|
||||||
|
"A=a",
|
||||||
|
[]query{
|
||||||
|
{"http://localhost:8910", "A=a"},
|
||||||
|
{"http://localhost:8910/", "A=a"},
|
||||||
|
{"http://localhost:8910/some/path", "A=a"},
|
||||||
|
{"https://localhost:8910", "A=a"},
|
||||||
|
{"https://localhost:8910/", "A=a"},
|
||||||
|
{"https://localhost:8910/some/path", "A=a"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Secure cookies are sent for localhost (tld)",
|
||||||
|
"http://example.LOCALHOST:8910/",
|
||||||
|
[]string{"A=a; secure"},
|
||||||
|
"A=a",
|
||||||
|
[]query{
|
||||||
|
{"http://example.LOCALHOST:8910", "A=a"},
|
||||||
|
{"http://example.LOCALHOST:8910/", "A=a"},
|
||||||
|
{"http://example.LOCALHOST:8910/some/path", "A=a"},
|
||||||
|
{"https://example.LOCALHOST:8910", "A=a"},
|
||||||
|
{"https://example.LOCALHOST:8910/", "A=a"},
|
||||||
|
{"https://example.LOCALHOST:8910/some/path", "A=a"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Secure cookies are sent for localhost (ipv6)",
|
||||||
|
"http://[::1]:8910/",
|
||||||
|
[]string{"A=a; secure"},
|
||||||
|
"A=a",
|
||||||
|
[]query{
|
||||||
|
{"http://[::1]:8910", "A=a"},
|
||||||
|
{"http://[::1]:8910/", "A=a"},
|
||||||
|
{"http://[::1]:8910/some/path", "A=a"},
|
||||||
|
{"https://[::1]:8910", "A=a"},
|
||||||
|
{"https://[::1]:8910/", "A=a"},
|
||||||
|
{"https://[::1]:8910/some/path", "A=a"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Localhost only if it's a segment",
|
||||||
|
"http://notlocalhost/",
|
||||||
|
[]string{"A=a; secure"},
|
||||||
|
"A=a",
|
||||||
|
[]query{
|
||||||
|
{"http://notlocalhost", ""},
|
||||||
|
{"http://notlocalhost/", ""},
|
||||||
|
{"http://notlocalhost/some/path", ""},
|
||||||
|
{"https://notlocalhost", "A=a"},
|
||||||
|
{"https://notlocalhost/", "A=a"},
|
||||||
|
{"https://notlocalhost/some/path", "A=a"},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Explicit path.",
|
"Explicit path.",
|
||||||
"http://www.host.test/",
|
"http://www.host.test/",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue