Exclude Cookie with non-ascii value in Cookie name or value
Guarded by net::feature flag DisallowNonAsciiCookies, when a cookie's name or value contains a non-ascii value, this cookie will be excluded. This feature is disabled by default. Bug: 40061459 Change-Id: Ib853b730568065a5a73d699a1cac51c548c72a40 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6123584 Reviewed-by: Maks Orlovich <morlovich@chromium.org> Reviewed-by: Steven Bingler <bingler@chromium.org> Commit-Queue: Amarjot Gill <amarjotgill@chromium.org> Cr-Commit-Position: refs/heads/main@{#1404400}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
2cdfb3b004
commit
f61674a72b
content/public/common
net
tools/metrics/histograms/metadata/cookie
@@ -96,6 +96,9 @@ GetSwitchDependentFeatureOverrides(const base::CommandLine& command_line) {
|
|||||||
{switches::kEnableExperimentalCookieFeatures,
|
{switches::kEnableExperimentalCookieFeatures,
|
||||||
std::cref(net::features::kEnableSchemeBoundCookies),
|
std::cref(net::features::kEnableSchemeBoundCookies),
|
||||||
base::FeatureList::OVERRIDE_ENABLE_FEATURE},
|
base::FeatureList::OVERRIDE_ENABLE_FEATURE},
|
||||||
|
{switches::kEnableExperimentalCookieFeatures,
|
||||||
|
std::cref(net::features::kDisallowNonAsciiCookies),
|
||||||
|
base::FeatureList::OVERRIDE_ENABLE_FEATURE},
|
||||||
|
|
||||||
// Test behavior for third-party cookie phaseout.
|
// Test behavior for third-party cookie phaseout.
|
||||||
{network::switches::kTestThirdPartyCookiePhaseout,
|
{network::switches::kTestThirdPartyCookiePhaseout,
|
||||||
|
@@ -502,6 +502,12 @@ BASE_FEATURE(kEnableSchemeBoundCookies,
|
|||||||
"EnableSchemeBoundCookies",
|
"EnableSchemeBoundCookies",
|
||||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||||
|
|
||||||
|
// Disallows cookies to have non ascii values in their name or value.
|
||||||
|
NET_EXPORT BASE_DECLARE_FEATURE(kDisallowNonAsciiCookies);
|
||||||
|
BASE_FEATURE(kDisallowNonAsciiCookies,
|
||||||
|
"kDisallowNonAsciiCookies",
|
||||||
|
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||||
|
|
||||||
BASE_FEATURE(kTimeLimitedInsecureCookies,
|
BASE_FEATURE(kTimeLimitedInsecureCookies,
|
||||||
"TimeLimitedInsecureCookies",
|
"TimeLimitedInsecureCookies",
|
||||||
base::FEATURE_DISABLED_BY_DEFAULT);
|
base::FEATURE_DISABLED_BY_DEFAULT);
|
||||||
|
@@ -541,6 +541,9 @@ NET_EXPORT BASE_DECLARE_FEATURE(kEnablePortBoundCookies);
|
|||||||
// enables domain cookie shadowing protection.
|
// enables domain cookie shadowing protection.
|
||||||
NET_EXPORT BASE_DECLARE_FEATURE(kEnableSchemeBoundCookies);
|
NET_EXPORT BASE_DECLARE_FEATURE(kEnableSchemeBoundCookies);
|
||||||
|
|
||||||
|
// Disallows cookies to have non ascii values in their name or value.
|
||||||
|
NET_EXPORT BASE_DECLARE_FEATURE(kDisallowNonAsciiCookies);
|
||||||
|
|
||||||
// Enables expiration duration limit (3 hours) for cookies on insecure websites.
|
// Enables expiration duration limit (3 hours) for cookies on insecure websites.
|
||||||
// This feature is a no-op unless kEnableSchemeBoundCookies is enabled.
|
// This feature is a no-op unless kEnableSchemeBoundCookies is enabled.
|
||||||
NET_EXPORT BASE_DECLARE_FEATURE(kTimeLimitedInsecureCookies);
|
NET_EXPORT BASE_DECLARE_FEATURE(kTimeLimitedInsecureCookies);
|
||||||
|
@@ -459,6 +459,14 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::Create(
|
|||||||
parsed_cookie.IsHttpOnly(), samesite, parsed_cookie.Priority(),
|
parsed_cookie.IsHttpOnly(), samesite, parsed_cookie.Priority(),
|
||||||
cookie_partition_key, source_scheme, source_port, source_type);
|
cookie_partition_key, source_scheme, source_port, source_type);
|
||||||
|
|
||||||
|
// Check if name or value contains any non-ascii values, exclude if they do.
|
||||||
|
if (base::FeatureList::IsEnabled(features::kDisallowNonAsciiCookies)) {
|
||||||
|
if (!base::IsStringASCII(cc->Name()) || !base::IsStringASCII(cc->Value())) {
|
||||||
|
status->AddExclusionReason(
|
||||||
|
CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(chlily): Log metrics.
|
// TODO(chlily): Log metrics.
|
||||||
if (!cc->IsCanonical()) {
|
if (!cc->IsCanonical()) {
|
||||||
status->AddExclusionReason(
|
status->AddExclusionReason(
|
||||||
@@ -469,7 +477,7 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::Create(
|
|||||||
RecordCookieSameSiteAttributeValueHistogram(samesite_string);
|
RecordCookieSameSiteAttributeValueHistogram(samesite_string);
|
||||||
|
|
||||||
// These metrics capture whether or not a cookie has a Non-ASCII character in
|
// These metrics capture whether or not a cookie has a Non-ASCII character in
|
||||||
// it.
|
// it, except if kDisallowNonAsciiCookies is enabled.
|
||||||
UMA_HISTOGRAM_BOOLEAN("Cookie.HasNonASCII.Name",
|
UMA_HISTOGRAM_BOOLEAN("Cookie.HasNonASCII.Name",
|
||||||
!base::IsStringASCII(cc->Name()));
|
!base::IsStringASCII(cc->Name()));
|
||||||
UMA_HISTOGRAM_BOOLEAN("Cookie.HasNonASCII.Value",
|
UMA_HISTOGRAM_BOOLEAN("Cookie.HasNonASCII.Value",
|
||||||
@@ -524,6 +532,14 @@ std::unique_ptr<CanonicalCookie> CanonicalCookie::CreateSanitizedCookie(
|
|||||||
net::CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER);
|
net::CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if name or value contains any non-ascii values, exclude if they do.
|
||||||
|
if (base::FeatureList::IsEnabled(features::kDisallowNonAsciiCookies)) {
|
||||||
|
if (!base::IsStringASCII(name) || !base::IsStringASCII(value)) {
|
||||||
|
status->AddExclusionReason(
|
||||||
|
CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate name and value against character set and size limit constraints.
|
// Validate name and value against character set and size limit constraints.
|
||||||
// If IsValidCookieNameValuePair identifies that `name` and/or `value` are
|
// If IsValidCookieNameValuePair identifies that `name` and/or `value` are
|
||||||
// invalid, it will add an ExclusionReason to `status`.
|
// invalid, it will add an ExclusionReason to `status`.
|
||||||
@@ -947,6 +963,13 @@ bool CanonicalCookie::IsCanonicalForFromStorage() const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if name or value contains any non-ascii values, fail if they do.
|
||||||
|
if (base::FeatureList::IsEnabled(features::kDisallowNonAsciiCookies)) {
|
||||||
|
if (!base::IsStringASCII(Name()) || !base::IsStringASCII(Value())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
url::CanonHostInfo canon_host_info;
|
url::CanonHostInfo canon_host_info;
|
||||||
std::string canonical_domain(CanonicalizeHost(Domain(), &canon_host_info));
|
std::string canonical_domain(CanonicalizeHost(Domain(), &canon_host_info));
|
||||||
|
|
||||||
|
@@ -336,6 +336,169 @@ TEST(CanonicalCookieTest, CreateInvalidUrl) {
|
|||||||
CookieInclusionStatus::ExclusionReason::EXCLUDE_FAILURE_TO_STORE));
|
CookieInclusionStatus::ExclusionReason::EXCLUDE_FAILURE_TO_STORE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that when net feature kDisallowNonAsciiCookies is enabled a cookie with
|
||||||
|
// a non-ascii value in its name or value is properly excluded.
|
||||||
|
TEST(CanonicalCookieTest, CreateNonAsciiCookieNameAndValue) {
|
||||||
|
base::Time creation_time = base::Time::Now();
|
||||||
|
std::optional<base::Time> server_time = std::nullopt;
|
||||||
|
CookieInclusionStatus status;
|
||||||
|
std::unique_ptr<CanonicalCookie> cc;
|
||||||
|
|
||||||
|
// Feature is not enabled yet so cookie with non-ascii name or value should
|
||||||
|
// still be included.
|
||||||
|
cc = CanonicalCookie::Create(GURL("https://www.foo.com/path"), "€=2",
|
||||||
|
creation_time, server_time,
|
||||||
|
/*cookie_partition_key=*/std::nullopt,
|
||||||
|
CookieSourceType::kUnknown, &status);
|
||||||
|
EXPECT_TRUE(cc.get());
|
||||||
|
EXPECT_TRUE(status.IsInclude());
|
||||||
|
cc = CanonicalCookie::Create(GURL("https://www.foo.com/path"), "A=€",
|
||||||
|
creation_time, server_time,
|
||||||
|
/*cookie_partition_key=*/std::nullopt,
|
||||||
|
CookieSourceType::kUnknown, &status);
|
||||||
|
EXPECT_TRUE(cc.get());
|
||||||
|
EXPECT_TRUE(status.IsInclude());
|
||||||
|
|
||||||
|
cc = CanonicalCookie::Create(GURL("https://www.foo.com/path"), "€=€",
|
||||||
|
creation_time, server_time,
|
||||||
|
/*cookie_partition_key=*/std::nullopt,
|
||||||
|
CookieSourceType::kUnknown, &status);
|
||||||
|
EXPECT_TRUE(cc.get());
|
||||||
|
EXPECT_TRUE(status.IsInclude());
|
||||||
|
|
||||||
|
// Since feature is not enabled this should be true.
|
||||||
|
EXPECT_TRUE(cc->IsCanonical());
|
||||||
|
|
||||||
|
base::test::ScopedFeatureList scoped_feature_list;
|
||||||
|
scoped_feature_list.InitWithFeatures(
|
||||||
|
{net::features::kDisallowNonAsciiCookies}, {});
|
||||||
|
|
||||||
|
// Now that feature is enabled this should return false.
|
||||||
|
EXPECT_FALSE(cc->IsCanonical());
|
||||||
|
|
||||||
|
// Valid cookie which should be included.
|
||||||
|
cc = CanonicalCookie::Create(GURL("https://www.foo.com/path"), "A=2",
|
||||||
|
creation_time, server_time,
|
||||||
|
/*cookie_partition_key=*/std::nullopt,
|
||||||
|
CookieSourceType::kUnknown, &status);
|
||||||
|
EXPECT_TRUE(cc.get());
|
||||||
|
EXPECT_TRUE(status.IsInclude());
|
||||||
|
// Now that feature is enabled this cookie will be excluded.
|
||||||
|
cc = CanonicalCookie::Create(GURL("https://www.foo.com/path"), "€=2",
|
||||||
|
creation_time, server_time,
|
||||||
|
/*cookie_partition_key=*/std::nullopt,
|
||||||
|
CookieSourceType::kUnknown, &status);
|
||||||
|
EXPECT_FALSE(cc.get());
|
||||||
|
EXPECT_TRUE(status.HasExclusionReason(
|
||||||
|
CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER));
|
||||||
|
|
||||||
|
// Now that feature is enabled this cookie will be excluded.
|
||||||
|
cc = CanonicalCookie::Create(GURL("https://www.foo.com/path"), "A=€",
|
||||||
|
creation_time, server_time,
|
||||||
|
/*cookie_partition_key=*/std::nullopt,
|
||||||
|
CookieSourceType::kUnknown, &status);
|
||||||
|
EXPECT_FALSE(cc.get());
|
||||||
|
EXPECT_TRUE(status.HasExclusionReason(
|
||||||
|
CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER));
|
||||||
|
|
||||||
|
// Now that feature is enabled this cookie will be excluded.
|
||||||
|
cc = CanonicalCookie::Create(GURL("https://www.foo.com/path"), "€=€",
|
||||||
|
creation_time, server_time,
|
||||||
|
/*cookie_partition_key=*/std::nullopt,
|
||||||
|
CookieSourceType::kUnknown, &status);
|
||||||
|
EXPECT_FALSE(cc.get());
|
||||||
|
EXPECT_TRUE(status.HasExclusionReason(
|
||||||
|
CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER));
|
||||||
|
|
||||||
|
cc = CanonicalCookie::CreateSanitizedCookie(
|
||||||
|
GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
|
||||||
|
base::Time(), base::Time(), base::Time(), false /*secure*/,
|
||||||
|
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
|
||||||
|
COOKIE_PRIORITY_DEFAULT, std::nullopt /*partition_key*/, &status);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that when net feature kDisallowNonAsciiCookies is enabled a cookie with
|
||||||
|
// a non-ascii value in its name or value is properly excluded.
|
||||||
|
TEST(CanonicalCookieTest, CreateSanitizedNonAsciiCookieNameAndValue) {
|
||||||
|
CookieInclusionStatus status;
|
||||||
|
std::unique_ptr<CanonicalCookie> cc;
|
||||||
|
|
||||||
|
// Feature is not enabled yet so cookie with non-ascii name or value should
|
||||||
|
// still be included.
|
||||||
|
cc = CanonicalCookie::CreateSanitizedCookie(
|
||||||
|
GURL("https://www.foo.com"), "€", "A", std::string(), "/foo",
|
||||||
|
base::Time(), base::Time(), base::Time(), false /*secure*/,
|
||||||
|
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
|
||||||
|
COOKIE_PRIORITY_DEFAULT, std::nullopt /*partition_key*/, &status);
|
||||||
|
|
||||||
|
EXPECT_TRUE(cc.get());
|
||||||
|
EXPECT_TRUE(status.IsInclude());
|
||||||
|
|
||||||
|
cc = CanonicalCookie::CreateSanitizedCookie(
|
||||||
|
GURL("https://www.foo.com"), "A", "€", std::string(), "/foo",
|
||||||
|
base::Time(), base::Time(), base::Time(), false /*secure*/,
|
||||||
|
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
|
||||||
|
COOKIE_PRIORITY_DEFAULT, std::nullopt /*partition_key*/, &status);
|
||||||
|
EXPECT_TRUE(cc.get());
|
||||||
|
EXPECT_TRUE(status.IsInclude());
|
||||||
|
|
||||||
|
cc = CanonicalCookie::CreateSanitizedCookie(
|
||||||
|
GURL("https://www.foo.com"), "€", "€", std::string(), "/foo",
|
||||||
|
base::Time(), base::Time(), base::Time(), false /*secure*/,
|
||||||
|
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
|
||||||
|
COOKIE_PRIORITY_DEFAULT, std::nullopt /*partition_key*/, &status);
|
||||||
|
EXPECT_TRUE(cc.get());
|
||||||
|
EXPECT_TRUE(status.IsInclude());
|
||||||
|
|
||||||
|
// Since feature is not enabled this should be true.
|
||||||
|
EXPECT_TRUE(cc->IsCanonical());
|
||||||
|
|
||||||
|
base::test::ScopedFeatureList scoped_feature_list;
|
||||||
|
scoped_feature_list.InitWithFeatures(
|
||||||
|
{net::features::kDisallowNonAsciiCookies}, {});
|
||||||
|
|
||||||
|
// Now that feature is enabled this should return false.
|
||||||
|
EXPECT_FALSE(cc->IsCanonical());
|
||||||
|
|
||||||
|
// Valid cookie which should be included.
|
||||||
|
cc = CanonicalCookie::CreateSanitizedCookie(
|
||||||
|
GURL("https://www.foo.com"), "A", "B", std::string(), "/foo",
|
||||||
|
base::Time(), base::Time(), base::Time(), false /*secure*/,
|
||||||
|
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
|
||||||
|
COOKIE_PRIORITY_DEFAULT, std::nullopt /*partition_key*/, &status);
|
||||||
|
EXPECT_TRUE(cc.get());
|
||||||
|
EXPECT_TRUE(status.IsInclude());
|
||||||
|
// Now that feature is enabled this cookie will be excluded.
|
||||||
|
cc = CanonicalCookie::CreateSanitizedCookie(
|
||||||
|
GURL("https://www.foo.com"), "€", "2", std::string(), "/foo",
|
||||||
|
base::Time(), base::Time(), base::Time(), false /*secure*/,
|
||||||
|
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
|
||||||
|
COOKIE_PRIORITY_DEFAULT, std::nullopt /*partition_key*/, &status);
|
||||||
|
EXPECT_FALSE(cc.get());
|
||||||
|
EXPECT_TRUE(status.HasExclusionReason(
|
||||||
|
CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER));
|
||||||
|
|
||||||
|
// Now that feature is enabled this cookie will be excluded.
|
||||||
|
cc = CanonicalCookie::CreateSanitizedCookie(
|
||||||
|
GURL("https://www.foo.com"), "A", "€", std::string(), "/foo",
|
||||||
|
base::Time(), base::Time(), base::Time(), false /*secure*/,
|
||||||
|
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
|
||||||
|
COOKIE_PRIORITY_DEFAULT, std::nullopt /*partition_key*/, &status);
|
||||||
|
EXPECT_FALSE(cc.get());
|
||||||
|
EXPECT_TRUE(status.HasExclusionReason(
|
||||||
|
CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER));
|
||||||
|
|
||||||
|
// Now that feature is enabled this cookie will be excluded.
|
||||||
|
cc = CanonicalCookie::CreateSanitizedCookie(
|
||||||
|
GURL("https://www.foo.com"), "€", "€", std::string(), "/foo",
|
||||||
|
base::Time(), base::Time(), base::Time(), false /*secure*/,
|
||||||
|
false /*httponly*/, CookieSameSite::NO_RESTRICTION,
|
||||||
|
COOKIE_PRIORITY_DEFAULT, std::nullopt /*partition_key*/, &status);
|
||||||
|
EXPECT_FALSE(cc.get());
|
||||||
|
EXPECT_TRUE(status.HasExclusionReason(
|
||||||
|
CookieInclusionStatus::EXCLUDE_DISALLOWED_CHARACTER));
|
||||||
|
}
|
||||||
|
|
||||||
// Test that a cookie string with an empty domain attribute generates a
|
// Test that a cookie string with an empty domain attribute generates a
|
||||||
// canonical host cookie.
|
// canonical host cookie.
|
||||||
TEST(CanonicalCookieTest, CreateHostCookieFromString) {
|
TEST(CanonicalCookieTest, CreateHostCookieFromString) {
|
||||||
|
@@ -591,7 +591,9 @@ chromium-metrics-reviews@google.com.
|
|||||||
If a cookie's {CookieField} field contains a non-ASCII character.
|
If a cookie's {CookieField} field contains a non-ASCII character.
|
||||||
Informally, this indicates if the field contains a (non-ASCII) Unicode
|
Informally, this indicates if the field contains a (non-ASCII) Unicode
|
||||||
character. Logged whenever a cookie is created via
|
character. Logged whenever a cookie is created via
|
||||||
CanonicalCookie::Create().
|
CanonicalCookie::Create(). When kDisallowNonAsciiCookies is enabled and a
|
||||||
|
cookie's {CookieField} field contains a non-ASCII character, this metric
|
||||||
|
will not be recorded.
|
||||||
</summary>
|
</summary>
|
||||||
<token key="CookieField">
|
<token key="CookieField">
|
||||||
<variant name="Name"/>
|
<variant name="Name"/>
|
||||||
|
Reference in New Issue
Block a user