Change cookie domain handling for IP address host
When the host name is an IP literal, the domain attribute value should
be string matched (ignoring case) against the domain and should also
ignore a leading dot if one exists.
Bug: 1257205
Change-Id: Id103a2f08035aa2972e0c55e5fc35141af145fd2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3332072
Reviewed-by: Steven Bingler <bingler@chromium.org>
Commit-Queue: Steven Bingler <bingler@chromium.org>
Cr-Commit-Position: refs/heads/main@{#958093}
diff --git a/net/cookies/canonical_cookie_unittest.cc b/net/cookies/canonical_cookie_unittest.cc
index 58ef5aa..633a324 100644
--- a/net/cookies/canonical_cookie_unittest.cc
+++ b/net/cookies/canonical_cookie_unittest.cc
@@ -432,6 +432,53 @@
{CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
}
+TEST(CanonicalCookieTest, CreateWithDomainAsIP) {
+ GURL url("http://1.1.1.1");
+ GURL url6("http://[2606:2800:220:1:248:1893:25c8:1946]");
+
+ base::Time now = base::Time::Now();
+ absl::optional<base::Time> server_time = absl::nullopt;
+ CookieInclusionStatus status;
+
+ const struct {
+ const GURL url;
+ const std::string cookie_line;
+ const bool expectedResult;
+ } kTests[] = {
+ {url, "d=1;Domain=1.1.1.1;", true},
+ {url, "dd=1;Domain=.1.1.1.1;", true},
+ {url, "ds=1;Domain=1.1.1;", false},
+ {url, "dsd=1;Domain=.1.1.1;", false},
+ {url, "dx=1;Domain=0x01.0x1.0x1.0x1;", false},
+ {url, "dxd=1;Domain=.0x01.0x1.0x1.0x1;", false},
+ {url, "do=1;Domain=0001.0001.0001.0001;", false},
+ {url, "d10=1;Domain=16843009;", false},
+ {url, "d16=value;Domain=0x1010101;", false},
+ {url, "d8=1;Domain=0100200401;", false},
+ {url, "dm=1;Domain=00001.0x01.1.001;", false},
+ {url6, "d1ipv6=1;Domain=[2606:2800:220:1:248:1893:25c8:1946];", true},
+ {url6, "dd1ipv6=1;Domain=.[2606:2800:220:1:248:1893:25c8:1946];", true},
+ {url6, "dc1ipv6=1;Domain=[2606:2800:220:1:248:1893:25C8:1946];", true},
+ {url6, "d2ipv6=1;Domain=2606:2800:220:1:248:1893:25c8:1946;", false},
+ {url6, "dd2ipv6=1;Domain=.2606:2800:220:1:248:1893:25c8:1946;", false},
+ {url6, "dc2ipv6=1;Domain=2606:2800:220:1:248:1893:25C8:1946;", false},
+ };
+
+ for (const auto& test : kTests) {
+ std::unique_ptr<CanonicalCookie> cookie = CanonicalCookie::Create(
+ test.url, test.cookie_line, now, server_time,
+ absl::nullopt /* cookie_partition_key */, &status);
+ if (test.expectedResult) {
+ ASSERT_TRUE(cookie.get());
+ EXPECT_EQ(test.url.host(), cookie->Domain());
+ } else {
+ EXPECT_EQ(nullptr, cookie.get());
+ EXPECT_TRUE(status.HasExactlyExclusionReasonsForTesting(
+ {CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN}));
+ }
+ }
+}
+
TEST(CanonicalCookieTest, CreateSameParty) {
GURL url("http://www.example.com/test/foo.html");
GURL https_url("https://www.example.com/test/foo.html");
@@ -2476,6 +2523,20 @@
COOKIE_PRIORITY_LOW, false)
->IsCanonical());
+ // Non-canonical IPv4 address as domain.
+ EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", "16843009", "/path", base::Time(), base::Time(),
+ base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
+ // Non-canonical IPv4 address as domain.
+ EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", "0x1010101", "/path", base::Time(), base::Time(),
+ base::Time(), false, false, CookieSameSite::NO_RESTRICTION,
+ COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
// Null IPv6 address as domain.
EXPECT_TRUE(CanonicalCookie::CreateUnsafeCookieForTesting(
"A", "B", "[::]", "/path", base::Time(), base::Time(),
@@ -2535,6 +2596,13 @@
CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW, false)
->IsCanonical());
+ // Missing square brackets in IPv6 address as domain.
+ EXPECT_FALSE(CanonicalCookie::CreateUnsafeCookieForTesting(
+ "A", "B", "2606:2800:220:1:248:1893:25c8:1946", "/path",
+ base::Time(), base::Time(), base::Time(), false, false,
+ CookieSameSite::NO_RESTRICTION, COOKIE_PRIORITY_LOW, false)
+ ->IsCanonical());
+
// Properly formatted host cookie.
EXPECT_TRUE(CanonicalCookie::CreateUnsafeCookieForTesting(
"__Host-A", "B", "x.y", "/", base::Time(), base::Time(),
diff --git a/net/cookies/cookie_store_unittest.h b/net/cookies/cookie_store_unittest.h
index 95fc0f9..7e155f4 100644
--- a/net/cookies/cookie_store_unittest.h
+++ b/net/cookies/cookie_store_unittest.h
@@ -1097,15 +1097,28 @@
TYPED_TEST_P(CookieStoreTest, TestIpAddressNoDomainCookies) {
GURL url_ip("http://1.2.3.4/weee");
CookieStore* cs = this->GetCookieStore();
- EXPECT_FALSE(this->SetCookie(cs, url_ip, "b=2; domain=.1.2.3.4"));
EXPECT_FALSE(this->SetCookie(cs, url_ip, "c=3; domain=.3.4"));
this->MatchCookieLines(std::string(), this->GetCookies(cs, url_ip));
// It should be allowed to set a cookie if domain= matches the IP address
- // exactly. This matches IE/Firefox, even though it seems a bit wrong.
+ // by ignoring case and ignoring a leading dot. This matches IE/Firefox, even
+ // though it seems a bit wrong.
EXPECT_FALSE(this->SetCookie(cs, url_ip, "b=2; domain=1.2.3.3"));
this->MatchCookieLines(std::string(), this->GetCookies(cs, url_ip));
EXPECT_TRUE(this->SetCookie(cs, url_ip, "b=2; domain=1.2.3.4"));
this->MatchCookieLines("b=2", this->GetCookies(cs, url_ip));
+ EXPECT_TRUE(this->SetCookie(cs, url_ip, "b=2; domain=.1.2.3.4"));
+ this->MatchCookieLines("b=2", this->GetCookies(cs, url_ip));
+
+#if !BUILDFLAG(IS_IOS)
+ // Test a couple of IPv6 addresses
+ GURL url_ip6("http://[2606:2800:220:1:248:1893:25c8:1946]");
+ EXPECT_FALSE(this->SetCookie(
+ cs, url_ip6, "e=1; domain=.2606:2800:220:1:248:1893:25c8:1946"));
+ this->MatchCookieLines(std::string(), this->GetCookies(cs, url_ip6));
+ EXPECT_TRUE(this->SetCookie(
+ cs, url_ip6, "d=1; domain=[2606:2800:220:1:248:1893:25c8:1946]"));
+ this->MatchCookieLines("d=1", this->GetCookies(cs, url_ip6));
+#endif // !BUILDFLAG(IS_IOS)
}
// Test a TLD setting cookies on itself.
diff --git a/net/cookies/cookie_util.cc b/net/cookies/cookie_util.cc
index c7bd72ae..7668bb5 100644
--- a/net/cookies/cookie_util.cc
+++ b/net/cookies/cookie_util.cc
@@ -283,15 +283,14 @@
const std::string& domain_string,
std::string* result) {
const std::string url_host(url.host());
-
- url::CanonHostInfo ignored;
- std::string cookie_domain(CanonicalizeHost(domain_string, &ignored));
-
// If no domain was specified in the domain string, default to a host cookie.
- // We match IE/Firefox in allowing a domain=IPADDR if it matches the url
- // ip address hostname exactly. It should be treated as a host cookie.
+ // We match IE/Firefox in allowing a domain=IPADDR if it matches (case
+ // in-sensitive) the url ip address hostname and ignoring a leading dot if one
+ // exists. It should be treated as a host cookie.
if (domain_string.empty() ||
- (url.HostIsIPAddress() && url_host == cookie_domain)) {
+ (url.HostIsIPAddress() &&
+ (base::EqualsCaseInsensitiveASCII(url_host, domain_string) ||
+ base::EqualsCaseInsensitiveASCII("." + url_host, domain_string)))) {
*result = url_host;
DCHECK(DomainIsHostOnly(*result));
return true;
@@ -303,6 +302,8 @@
return false;
}
+ url::CanonHostInfo ignored;
+ std::string cookie_domain(CanonicalizeHost(domain_string, &ignored));
// Get the normalized domain specified in cookie line.
if (cookie_domain.empty())
return false;