0

Introduce CompressionDictionaryTransportRequireKnownRootCert flag

We are observing potential issues with CompressionDictionaryTransport
feature that may be caused by network appliances that intercept network
traffic.

To investigate this issue, this CL introduces a new feature flag named
CompressionDictionaryTransportRequireKnownRootCert. If this feature is
enabled, Chromium will use stored shared dictionaries only when the
HTTPS connection's certificate is rooted by a well known root CA or when
the server is localhost.

Bug: 1413922
Change-Id: Iaaa9d4db9d335cde12632925c1e36b2a55a4cf9d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5028336
Reviewed-by: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: Christian Dullweber <dullweber@chromium.org>
Commit-Queue: Tsuyoshi Horo <horo@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1232707}
This commit is contained in:
Tsuyoshi Horo
2023-12-04 15:07:05 +00:00
committed by Chromium LUCI CQ
parent 5e712ee37d
commit 2cce8d5236
12 changed files with 201 additions and 5 deletions

@ -10586,6 +10586,15 @@ const FeatureEntry kFeatureEntries[] = {
FEATURE_VALUE_TYPE(
network::features::kCompressionDictionaryTransportOverHttp1)},
{"enable-compression-dictionary-transport-require-known-root-cert",
flag_descriptions::kCompressionDictionaryTransportRequireKnownRootCertName,
flag_descriptions::
kCompressionDictionaryTransportRequireKnownRootCertDescription,
kOsAll,
FEATURE_VALUE_TYPE(
network::features::
kCompressionDictionaryTransportRequireKnownRootCert)},
{"enable-compute-pressure-rate-obfuscation-mitigation",
flag_descriptions::kComputePressureRateObfuscationMitigationName,
flag_descriptions::kComputePressureRateObfuscationMitigationDescription,

@ -281,7 +281,11 @@ class BrowsingDataModelBrowserTest
{network::features::kCompressionDictionaryTransportOverHttp1, {}},
};
std::vector<FeatureRef> disabled_features = {};
std::vector<FeatureRef> disabled_features = {
// Need to disable kCompressionDictionaryTransportRequireKnownRootCert
// because EmbeddedTestServer's certificate is not rooted at a standard
// CA root.
network::features::kCompressionDictionaryTransportRequireKnownRootCert};
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
enabled_features.push_back({media::kExternalClearKeyForTesting, {}});

@ -2257,6 +2257,11 @@
"owners": [ "horo@chromium.org", "net-dev@chromium.org" ],
"expiry_milestone": 130
},
{
"name": "enable-compression-dictionary-transport-require-known-root-cert",
"owners": [ "horo@chromium.org", "net-dev@chromium.org" ],
"expiry_milestone": 130
},
{
"name": "enable-compute-pressure-break-calibration-mitigation",
"owners": [ "arnaud.mandy@intel.com" ],

@ -922,6 +922,13 @@ const char kCompressionDictionaryTransportOverHttp1Description[] =
"When this is enabled, Chromium can use stored shared dictionaries even "
"when the connection is using HTTP/1 for non-localhost requests.";
const char kCompressionDictionaryTransportRequireKnownRootCertName[] =
"Compression dictionary transport require knwon root cert";
const char kCompressionDictionaryTransportRequireKnownRootCertDescription[] =
"When this is enabled, Chromium can use stored shared dictionaries only "
"when the connection is using a well known root cert or when the server is "
"a localhost.";
const char kForceColorProfileSRGB[] = "sRGB";
const char kForceColorProfileP3[] = "Display P3 D65";
const char kForceColorProfileRec2020[] = "ITU-R BT.2020";

@ -515,6 +515,10 @@ extern const char kCompressionDictionaryTransportBackendDescription[];
extern const char kCompressionDictionaryTransportOverHttp1Name[];
extern const char kCompressionDictionaryTransportOverHttp1Description[];
extern const char kCompressionDictionaryTransportRequireKnownRootCertName[];
extern const char
kCompressionDictionaryTransportRequireKnownRootCertDescription[];
extern const char kUseDMSAAForTilesName[];
extern const char kUseDMSAAForTilesDescription[];

@ -155,13 +155,18 @@ Shared Zstandard can be enabled/disabled from
## Supported HTTP protocol
From Chrome 121, Chrome may not use stored shared dictionares when the
connection is using HTTP/1 for non-localhost requests. This is a mitigation for
a issue that some network appliances are interfering with HTTPS traffic by
inspecting encrypted responses but failing to properly decode the shared
dictionary encoded content.
connection is using HTTP/1 for non-localhost requests. Also Chrome may not use
shared dictionares when the HTTPS connection's certificate is not rooted by a
well known root CA (eg: using a user installed root certificate). This is for
an investigation for an issue that some network appliances are interfering with
HTTPS traffic by inspecting encrypted responses but failing to properly decode
the shared dictionary encoded content.
If you want to use shared dictionaries with HTTP/1, please enable
[chrome://flags/#enable-compression-dictionary-transport-over-http1][over-http1-flag].
Also if you want to use shared dictionaries over the HTTPS connection which
certificate is not rooted by a well known root CA, please disable
[chrome://flags/#enable-compression-dictionary-transport-require-known-root-cert][require-known-root-ca-flag].
## Debugging
@ -202,6 +207,7 @@ There are a few demo sites that you can use to test the feature:
[backend-flag]: chrome://flags/#enable-compression-dictionary-transport-backend
[shared-zstd-flag]: chrome://flags/#enable-shared-zstd
[over-http1-flag]: chrome://flags/#enable-compression-dictionary-transport-over-http1
[require-known-root-ca-flag]: chrome://flags/#enable-compression-dictionary-transport-require-known-root-cert
[shared_dictionary_readme]: ../../services/network/shared_dictionary/README.md#flags
[ot-blog]: https://developer.chrome.com/blog/origin-trials/
[ot-console]: https://developer.chrome.com/origintrials/#/trials/active

@ -408,6 +408,13 @@ BASE_FEATURE(kCompressionDictionaryTransportOverHttp1,
"CompressionDictionaryTransportOverHttp1",
base::FEATURE_DISABLED_BY_DEFAULT);
// When this feature is enabled, Chromium will use stored shared dictionaries
// only if the request URL is a localhost URL or the transport layer is using a
// certificate rooted at a standard CA root.
BASE_FEATURE(kCompressionDictionaryTransportRequireKnownRootCert,
"CompressionDictionaryTransportRequireKnownRootCert",
base::FEATURE_DISABLED_BY_DEFAULT);
BASE_FEATURE(kVisibilityAwareResourceScheduler,
"VisibilityAwareResourceScheduler",
base::FEATURE_DISABLED_BY_DEFAULT);

@ -152,6 +152,8 @@ COMPONENT_EXPORT(NETWORK_CPP)
BASE_DECLARE_FEATURE(kCompressionDictionaryTransport);
COMPONENT_EXPORT(NETWORK_CPP)
BASE_DECLARE_FEATURE(kCompressionDictionaryTransportOverHttp1);
COMPONENT_EXPORT(NETWORK_CPP)
BASE_DECLARE_FEATURE(kCompressionDictionaryTransportRequireKnownRootCert);
// Enables visibility aware network service resource scheduler. When enabled,
// request may be prioritized or de-prioritized based on the visibility of

@ -195,6 +195,13 @@ void SharedDictionaryNetworkTransaction::ModifyRequestHeaders(
shared_dictionary_.reset();
return;
}
if (base::FeatureList::IsEnabled(
network::features::
kCompressionDictionaryTransportRequireKnownRootCert) &&
!cert_is_issued_by_known_root_ && !net::IsLocalhost(request_url)) {
shared_dictionary_.reset();
return;
}
// `is_shared_dictionary_read_allowed_callback_` triggers a notification of
// the shared dictionary usage to the browser process. So we need to call

@ -24,6 +24,7 @@
#include "services/network/shared_dictionary/shared_dictionary_manager.h"
#include "services/network/shared_dictionary/shared_dictionary_storage.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace network {
@ -380,6 +381,126 @@ TEST_F(SharedDictionaryNetworkTransactionTest, NotAllowedToUseDictionary) {
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_F(SharedDictionaryNetworkTransactionTest,
RequireKnownRootCertCheckFailure) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
network::features::kCompressionDictionaryTransportRequireKnownRootCert);
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// Override MockTransaction to check that there is no sec-available-dictionary
// header.
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.handler =
kTestTransactionHandlerWithoutAvailableDictionary;
new_mock_transaction.transport_info.cert_is_issued_by_known_root = false;
net::AddMockTransaction(&new_mock_transaction);
net::MockHttpRequest request(new_mock_transaction);
SharedDictionaryNetworkTransaction transaction(manager,
CreateNetworkTransaction());
transaction.SetIsSharedDictionaryReadAllowedCallback(
base::BindRepeating([]() { return true; }));
net::TestCompletionCallback start_callback;
ASSERT_THAT(transaction.Start(&request, start_callback.callback(),
net::NetLogWithSource()),
net::test::IsError(net::ERR_IO_PENDING));
EXPECT_THAT(start_callback.WaitForResult(), net::test::IsError(net::OK));
scoped_refptr<net::IOBufferWithSize> buf =
base::MakeRefCounted<net::IOBufferWithSize>(kDefaultBufferSize);
net::TestCompletionCallback read_callback;
ASSERT_THAT(
transaction.Read(buf.get(), buf->size(), read_callback.callback()),
net::test::IsError(net::ERR_IO_PENDING));
int read_result = read_callback.WaitForResult();
EXPECT_THAT(read_result, kTestData.size());
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_F(SharedDictionaryNetworkTransactionTest,
RequireKnownRootCertCheckSuccess) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
network::features::kCompressionDictionaryTransportRequireKnownRootCert);
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// The BrotliTestTransactionHandler `new_mock_transaction.handler` will check
// that the there is a correct sec-available-dictionary request header.
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.transport_info.cert_is_issued_by_known_root = true;
net::AddMockTransaction(&new_mock_transaction);
net::MockHttpRequest request(new_mock_transaction);
SharedDictionaryNetworkTransaction transaction(manager,
CreateNetworkTransaction());
transaction.SetIsSharedDictionaryReadAllowedCallback(
base::BindRepeating([]() { return true; }));
net::TestCompletionCallback start_callback;
ASSERT_THAT(transaction.Start(&request, start_callback.callback(),
net::NetLogWithSource()),
net::test::IsError(net::ERR_IO_PENDING));
EXPECT_THAT(start_callback.WaitForResult(), net::test::IsError(net::OK));
scoped_refptr<net::IOBufferWithSize> buf =
base::MakeRefCounted<net::IOBufferWithSize>(kDefaultBufferSize);
net::TestCompletionCallback read_callback;
ASSERT_THAT(
transaction.Read(buf.get(), buf->size(), read_callback.callback()),
net::test::IsError(net::ERR_IO_PENDING));
int read_result = read_callback.WaitForResult();
EXPECT_THAT(read_result, kTestData.size());
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_F(SharedDictionaryNetworkTransactionTest,
RequireKnownRootCertCheckSuccessForLocalhost) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeature(
network::features::kCompressionDictionaryTransportRequireKnownRootCert);
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(
std::make_unique<DummySyncDictionary>(kTestDictionaryData)));
// The BrotliTestTransactionHandler `new_mock_transaction.handler` will check
// that the there is a correct sec-available-dictionary request header.
net::MockTransaction new_mock_transaction = kBrotliDictionaryTestTransaction;
new_mock_transaction.url = "http:///localhost:1234/test";
new_mock_transaction.transport_info.cert_is_issued_by_known_root = false;
net::AddMockTransaction(&new_mock_transaction);
net::MockHttpRequest request(new_mock_transaction);
SharedDictionaryNetworkTransaction transaction(manager,
CreateNetworkTransaction());
transaction.SetIsSharedDictionaryReadAllowedCallback(
base::BindRepeating([]() { return true; }));
net::TestCompletionCallback start_callback;
ASSERT_THAT(transaction.Start(&request, start_callback.callback(),
net::NetLogWithSource()),
net::test::IsError(net::ERR_IO_PENDING));
EXPECT_THAT(start_callback.WaitForResult(), net::test::IsError(net::OK));
scoped_refptr<net::IOBufferWithSize> buf =
base::MakeRefCounted<net::IOBufferWithSize>(kDefaultBufferSize);
net::TestCompletionCallback read_callback;
ASSERT_THAT(
transaction.Read(buf.get(), buf->size(), read_callback.callback()),
net::test::IsError(net::ERR_IO_PENDING));
int read_result = read_callback.WaitForResult();
EXPECT_THAT(read_result, kTestData.size());
EXPECT_EQ(kTestData, std::string(buf->data(), read_result));
}
TEST_F(SharedDictionaryNetworkTransactionTest, NoMatchingDictionary) {
DummySharedDictionaryManager manager(
base::MakeRefCounted<DummySharedDictionaryStorage>(nullptr));

@ -4355,6 +4355,26 @@
]
}
],
"CompressionDictionaryTransportRequireKnownRootCert": [
{
"platforms": [
"android",
"chromeos",
"chromeos_lacros",
"linux",
"mac",
"windows"
],
"experiments": [
{
"name": "Enabled",
"enable_features": [
"CompressionDictionaryTransportRequireKnownRootCert"
]
}
]
}
],
"ConfigurableV8CodeCacheHotHours": [
{
"platforms": [

@ -32511,6 +32511,8 @@ from previous Chrome versions.
<int value="-1856902397" label="LoadingWithMojo:enabled"/>
<int value="-1856118202" label="AdvancedDocumentScanAPI:disabled"/>
<int value="-1855347512" label="FormControlsRefresh:disabled"/>
<int value="-1855339525"
label="CompressionDictionaryTransportRequireKnownRootCert:enabled"/>
<int value="-1854432127" label="ChromeHomePullToRefreshIphAtTop:disabled"/>
<int value="-1854372227" label="VrBrowsingExperimentalFeatures:enabled"/>
<int value="-1853877370" label="CastStreamingVp9:disabled"/>
@ -32892,6 +32894,8 @@ from previous Chrome versions.
<int value="-1660884825" label="ReadLaterNewBadgePromo:enabled"/>
<int value="-1659445938" label="RealboxSecondaryZeroSuggest:enabled"/>
<int value="-1658461830" label="SharedHighlightingRefinedBlocklist:enabled"/>
<int value="-1658154886"
label="CompressionDictionaryTransportRequireKnownRootCert:disabled"/>
<int value="-1657056532" label="FilesNewDirectoryTree:disabled"/>
<int value="-1656667928"
label="UseSyncInvalidationsForWalletAndOffer:disabled"/>