Skip to content

Commit

Permalink
[Private Network Access] Add use counter for 0.0.0.0
Browse files Browse the repository at this point in the history
In security perspective, we intend to block the usage of 0.0.0.0
entirely. Before that, we add a use counter here to track the usage
to estimate the impact.

Bug: 1300021
Change-Id: I563ac5dac6a47946b62a659d58c4a08f315c17bf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3557995
Reviewed-by: Mike West <mkwst@chromium.org>
Reviewed-by: Titouan Rigoudy <titouan@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Commit-Queue: Yifan Luo <lyf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#991571}
  • Loading branch information
iVanlIsh authored and Chromium LUCI CQ committed Apr 12, 2022
1 parent 6d60434 commit 5746e52
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 0 deletions.
95 changes: 95 additions & 0 deletions chrome/browser/net/private_network_access_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,101 @@ IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
}));
}

// Test the experimental use counter for accesses to the 0.0.0.0 IP address
// (and the corresponding `[::]` IPv6 address).
//
// In the Internet Protocol Version 4, the address 0.0.0.0 is a non-routable
// meta-address used to designate an invalid, unknown or non-applicable target.
// The real life behavior for 0.0.0.0 is different between operating systems.
// On Windows, it is unreachable, while on MacOS and Linux, 0.0.0.0 means
// all IP addresses on the local machine.
//
// In this case, 0.0.0.0 can be used to access localhost on MacOS and Linux
// and bypass Private Network Access checks, so that we would like to forbid
// fetches to 0.0.0.0. See more: https://crbug.com/1300021
#if BUILDFLAG(IS_WIN)
#define MAYBE_FetchNullIpAddressForNavigation \
DISABLED_FetchNullIpAddressForNavigation
#else
#define MAYBE_FetchNullIpAddressForNavigation FetchNullIpAddressForNavigation
#endif
IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
MAYBE_FetchNullIpAddressForNavigation) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();

EXPECT_TRUE(content::NavigateToURL(
web_contents(), server->GetURL("0.0.0.0", kNoFaviconPath)));

feature_histogram_tester.ExpectCounts(
AddFeatureCounts(AllZeroFeatureCounts(AllAddressSpaceFeatures()),
{
{WebFeature::kPrivateNetworkAccessNullIpAddress, 1},
}));
}

#if BUILDFLAG(IS_WIN)
#define MAYBE_FetchNullIpAddressFromDocument \
DISABLED_FetchNullIpAddressFromDocument
#else
#define MAYBE_FetchNullIpAddressFromDocument FetchNullIpAddressFromDocument
#endif
IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
MAYBE_FetchNullIpAddressFromDocument) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();

EXPECT_TRUE(
content::NavigateToURL(web_contents(), server->GetURL(kNoFaviconPath)));

auto subresource_url =
server->GetURL("0.0.0.0", "/set-header?Access-Control-Allow-Origin: *");
constexpr char kSubresourceScript[] = R"(
new Promise(resolve => {
fetch($1).then(e => resolve(true));
}))";
EXPECT_EQ(true, content::EvalJs(
web_contents(),
content::JsReplace(kSubresourceScript, subresource_url)));

feature_histogram_tester.ExpectCounts(
AddFeatureCounts(AllZeroFeatureCounts(AllAddressSpaceFeatures()),
{
{WebFeature::kPrivateNetworkAccessNullIpAddress, 1},
}));
}

#if BUILDFLAG(IS_WIN)
#define MAYBE_FetchNullIpAddressFromWorker DISABLED_FetchNullIpAddressFromWorker
#else
#define MAYBE_FetchNullIpAddressFromWorker FetchNullIpAddressFromWorker
#endif
IN_PROC_BROWSER_TEST_F(PrivateNetworkAccessWithFeatureEnabledBrowserTest,
MAYBE_FetchNullIpAddressFromWorker) {
WebFeatureHistogramTester feature_histogram_tester;
std::unique_ptr<net::EmbeddedTestServer> server = NewServer();

EXPECT_TRUE(content::NavigateToURL(
web_contents(), server->GetURL("/workers/fetch_from_worker.html")));

constexpr char kWorkerScript[] = R"(
new Promise(resolve => {
fetch_from_worker($1);
resolve(true);
}))";
auto worker_url =
server->GetURL("0.0.0.0", "/set-header?Access-Control-Allow-Origin: *");
EXPECT_EQ(true,
content::EvalJs(web_contents(),
content::JsReplace(kWorkerScript, worker_url)));

feature_histogram_tester.ExpectCounts(
AddFeatureCounts(AllZeroFeatureCounts(AllAddressSpaceFeatures()),
{
{WebFeature::kPrivateNetworkAccessNullIpAddress, 1},
}));
}

// ====================
// SPECIAL SCHEME TESTS
// ====================
Expand Down
5 changes: 5 additions & 0 deletions content/browser/renderer_host/navigation_request.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5967,6 +5967,11 @@ void NavigationRequest::UpdatePrivateNetworkRequestPolicy() {
DCHECK(!IsSameDocument());
DCHECK(!IsPageActivation());

if (GetSocketAddress().address().IsZero()) {
web_features_to_log_.push_back(
blink::mojom::WebFeature::kPrivateNetworkAccessNullIpAddress);
}

ContentBrowserClient* client = GetContentClient()->browser();
BrowserContext* context =
frame_tree_node_->navigator().controller().GetBrowserContext();
Expand Down
6 changes: 6 additions & 0 deletions docs/security/web-mitigation-metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,4 +202,10 @@ or services running on the user's machine directly.
- `kPrivateNetworkAccessFetchesWorkerScript`
- `kPrivateNetworkAccessWithWorker`

* `kPrivateNetworkAccessNullIpAddress` is an experimental use counter for
accesses to the 0.0.0.0 IP address (and the corresponding `[::]` IPv6 address).
These can be used to access localhost on MacOS and Linux and bypass Private
Network Access checks. We intent to block all such requests. See
https://crbug.com/1300021 and https://github.com/whatwg/fetch/issues/1117.

[pna]: https://wicg.github.io/private-network-access/
Original file line number Diff line number Diff line change
Expand Up @@ -3521,6 +3521,7 @@ enum WebFeature {
kOldConstraintIgnored = 4200,
kExplicitOverflowVisibleOnReplacedElement = 4201,
kExplicitOverflowVisibleOnReplacedElementWithObjectProp = 4202,
kPrivateNetworkAccessNullIpAddress = 4203,

// Add new features immediately above this line. Don't change assigned
// numbers of any item, and don't reuse removed slots.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ void RecordAddressSpaceFeature(LocalFrame* client_frame,
}

LocalDOMWindow* window = client_frame->DomWindow();

if (response.RemoteIPEndpoint().address().IsZero()) {
UseCounter::Count(window, WebFeature::kPrivateNetworkAccessNullIpAddress);
}

absl::optional<WebFeature> feature = AddressSpaceFeature(
FetchType::kSubresource, response.ClientAddressSpace(),
window->IsSecureContext(), response.AddressSpace());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ void ResourceLoadObserverForWorker::DidChangePriority(
void RecordPrivateNetworkAccessFeature(ExecutionContext* execution_context,
const ResourceResponse& response) {
DCHECK(execution_context);

if (response.RemoteIPEndpoint().address().IsZero()) {
execution_context->CountUse(WebFeature::kPrivateNetworkAccessNullIpAddress);
}

if (!network::IsLessPublicAddressSpace(response.AddressSpace(),
response.ClientAddressSpace()))
return;
Expand Down
1 change: 1 addition & 0 deletions tools/metrics/histograms/enums.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37792,6 +37792,7 @@ Called by update_use_counter_feature_enum.py.-->
<int value="4201" label="ExplicitOverflowVisibleOnReplacedElement"/>
<int value="4202"
label="ExplicitOverflowVisibleOnReplacedElementWithObjectProp"/>
<int value="4203" label="PrivateNetworkAccessNullIpAddress"/>
</enum>

<enum name="FeaturePolicyAllowlistType">
Expand Down

0 comments on commit 5746e52

Please sign in to comment.