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:

committed by
Chromium LUCI CQ

parent
5e712ee37d
commit
2cce8d5236
chrome/browser
docs/experiments
services/network
public
shared_dictionary
testing/variations
tools/metrics/histograms
@ -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"/>
|
||||
|
Reference in New Issue
Block a user