Commit b088a0ad authored by Titouan Rigoudy's avatar Titouan Rigoudy Committed by Commit Bot

[CORS-RFC1918] Add ResourceResponse::AddressSpace().

This extracts various computations of the response address space spread
out around third_party/blink/renderer to content/renderer/loader. It
prepares for moving that computation to services/network, alongside the
CORS-RFC1918 checks that compute the authoritative version of response's
address space.

This also enables easily extending the current UseCounters in
MixedContentChecker to cover the private-embedded-in-public case.

Bug: chromium:955213, chromium:1124358
Change-Id: I92642c2da8c062a51afd1fb6a783b164c4cd904b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2424083
Commit-Queue: Titouan Rigoudy <titouan@chromium.org>
Auto-Submit: Titouan Rigoudy <titouan@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810621}
parent 447e31fa
......@@ -58,7 +58,9 @@
#include "net/ssl/ssl_connection_status_flags.h"
#include "net/ssl/ssl_info.h"
#include "services/network/public/cpp/http_raw_request_response_info.h"
#include "services/network/public/cpp/ip_address_space_util.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/ip_address_space.mojom-shared.h"
#include "services/network/public/mojom/trust_tokens.mojom-shared.h"
#include "services/network/public/mojom/url_loader.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
......@@ -917,9 +919,6 @@ void WebURLLoaderImpl::PopulateURLResponse(
response->SetWasCached(!head.load_timing.request_start_time.is_null() &&
head.response_time <
head.load_timing.request_start_time);
response->SetRemoteIPAddress(WebString::FromUTF8(
net::HostPortPair::FromIPEndPoint(head.remote_endpoint).HostForURL()));
response->SetRemotePort(head.remote_endpoint.port());
response->SetConnectionID(head.load_timing.socket_log_id);
response->SetConnectionReused(head.load_timing.socket_reused);
response->SetWasFetchedViaSPDY(head.was_fetched_via_spdy);
......@@ -934,6 +933,26 @@ void WebURLLoaderImpl::PopulateURLResponse(
network::mojom::FetchResponseSource::kCacheStorage
? blink::WebString::FromUTF8(head.cache_storage_cache_name)
: blink::WebString());
response->SetRemoteIPAddress(WebString::FromUTF8(
net::HostPortPair::FromIPEndPoint(head.remote_endpoint).HostForURL()));
response->SetRemotePort(head.remote_endpoint.port());
// This computation can only be done once SetUrlListViaServiceWorker() has
// been called on |response|, so that ResponseUrl() returns the correct
// answer.
//
// Implements: https://wicg.github.io/cors-rfc1918/#integration-html
//
// TODO(crbug.com/955213): Just copy the address space in |head| once it is
// made available.
if (response->ResponseUrl().ProtocolIs("file")) {
response->SetAddressSpace(network::mojom::IPAddressSpace::kLocal);
} else {
response->SetAddressSpace(
network::IPAddressToIPAddressSpace(head.remote_endpoint.address()));
}
blink::WebVector<blink::WebString> cors_exposed_header_names(
head.cors_exposed_header_names.size());
std::transform(
......
......@@ -41,6 +41,7 @@
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
#include "third_party/blink/public/platform/web_data.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_error.h"
#include "third_party/blink/public/platform/web_url_loader_client.h"
#include "third_party/blink/public/platform/web_url_request.h"
......@@ -483,6 +484,72 @@ TEST_F(WebURLLoaderImplTest, ResponseIPAddress) {
};
}
TEST_F(WebURLLoaderImplTest, ResponseAddressSpace) {
using AddressSpace = network::mojom::IPAddressSpace;
struct TestCase {
std::string url;
std::string ip;
AddressSpace expected;
} cases[] = {
{"http://localhost", "127.0.0.1", AddressSpace::kLocal},
{"http://localhost", "::1", AddressSpace::kLocal},
{"file:///a/path", "", AddressSpace::kLocal},
{"file:///a/path", "8.8.8.8", AddressSpace::kLocal},
{"http://router.local", "10.1.0.1", AddressSpace::kPrivate},
{"http://router.local", "::ffff:192.0.2.128", AddressSpace::kPrivate},
{"https://bleep.test", "8.8.8.8", AddressSpace::kPublic},
{"http://a.test", "2001:db8:85a3::8a2e:370:7334", AddressSpace::kPublic},
{"http://invalid", "", AddressSpace::kUnknown},
};
for (const auto& test : cases) {
SCOPED_TRACE(test.url + ", " + test.ip);
GURL url(test.url);
// We are forced to use the result of AssignFromIPLiteral(), and we cannot
// just assign it to an unused variable. Check that all non-empty literals
// are correctly parsed.
net::IPAddress address;
EXPECT_EQ(!test.ip.empty(), address.AssignFromIPLiteral(test.ip));
network::mojom::URLResponseHead head;
head.remote_endpoint = net::IPEndPoint(address, 443);
blink::WebURLResponse response;
WebURLLoaderImpl::PopulateURLResponse(url, head, &response, true, -1);
EXPECT_EQ(test.expected, response.AddressSpace());
}
}
// This test verifies that the IPAddressSpace set on WebURLResponse takes into
// account WebURLResponse::ResponseUrl() instead of
// WebURLResponse::CurrentRequestUrl().
TEST_F(WebURLLoaderImplTest, ResponseAddressSpaceConsidersResponseUrl) {
GURL request_url("http://request.test");
// The remote endpoint contains a public IP address, but the response was
// ultimately fetched by a service worker from a file URL.
network::mojom::URLResponseHead head;
head.remote_endpoint = net::IPEndPoint(net::IPAddress(8, 8, 8, 8), 80);
head.was_fetched_via_service_worker = true;
head.url_list_via_service_worker = {
GURL("http://redirect.test"),
GURL("file:///a/path"),
};
blink::WebURLResponse response;
WebURLLoaderImpl::PopulateURLResponse(request_url, head, &response, true, -1);
// The address space of the response reflects the fact the it was fetched
// from a file, even though the request was initially to a public website.
EXPECT_EQ(GURL("http://request.test"), GURL(response.CurrentRequestUrl()));
EXPECT_EQ(GURL("file:///a/path"), GURL(response.ResponseUrl()));
EXPECT_EQ(network::mojom::IPAddressSpace::kLocal, response.AddressSpace());
}
TEST_F(WebURLLoaderImplTest, ResponseCert) {
GURL url("https://test.example/");
......
......@@ -46,6 +46,7 @@ include_rules = [
"+services/network/public/mojom/cors.mojom-shared.h",
"+services/network/public/mojom/data_pipe_getter.mojom-shared.h",
"+services/network/public/mojom/fetch_api.mojom-shared.h",
"+services/network/public/mojom/ip_address_space.mojom-shared.h",
"+services/network/public/mojom/referrer_policy.mojom-shared.h",
"+services/network/public/mojom/trust_tokens.mojom-shared.h",
"+services/network/public/mojom/url_loader_factory.mojom-shared.h",
......
......@@ -46,6 +46,7 @@ namespace network {
namespace mojom {
enum class FetchResponseSource;
enum class FetchResponseType : int32_t;
enum class IPAddressSpace : int32_t;
class LoadTimingInfo;
}
} // namespace network
......@@ -293,6 +294,10 @@ class WebURLResponse {
BLINK_PLATFORM_EXPORT uint16_t RemotePort() const;
BLINK_PLATFORM_EXPORT void SetRemotePort(uint16_t);
// Address space from which this resource was fetched.
BLINK_PLATFORM_EXPORT network::mojom::IPAddressSpace AddressSpace() const;
BLINK_PLATFORM_EXPORT void SetAddressSpace(network::mojom::IPAddressSpace);
// ALPN negotiated protocol of the socket which fetched this resource.
BLINK_PLATFORM_EXPORT bool WasAlpnNegotiated() const;
BLINK_PLATFORM_EXPORT void SetWasAlpnNegotiated(bool);
......
......@@ -74,6 +74,7 @@ include_rules = [
"+services/metrics/public",
"+services/network/public/cpp/cors/cors_error_status.h",
"+services/network/public/cpp/features.h",
"+services/network/public/cpp/ip_address_space_util.h",
"+services/network/public/cpp/request_destination.h",
"+services/network/public/cpp/request_mode.h",
"+services/network/public/cpp/web_sandbox_flags.h",
......
......@@ -1254,8 +1254,7 @@ void DocumentLoader::StartLoadingInternal() {
HandleRedirect(redirect_response.CurrentRequestUrl());
}
MixedContentChecker::CheckMixedPrivatePublic(GetFrame(),
response_.RemoteIPAddress());
MixedContentChecker::CheckMixedPrivatePublic(GetFrame(), response_);
ApplyClientHintsConfig(params_->enabled_client_hints);
PreloadHelper::LoadLinksFromHeader(
response_.HttpHeaderField(http_names::kLink),
......
......@@ -30,6 +30,7 @@
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "services/network/public/cpp/ip_address_space_util.h"
#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/security_context/insecure_request_policy.h"
......@@ -785,26 +786,24 @@ bool MixedContentChecker::ShouldAutoupgrade(
void MixedContentChecker::CheckMixedPrivatePublic(
LocalFrame* frame,
const AtomicString& resource_ip_address) {
const ResourceResponse& response) {
if (!frame)
return;
// Just count these for the moment, don't block them.
if (network_utils::IsReservedIPAddress(resource_ip_address) &&
frame->DomWindow()->AddressSpace() ==
network::mojom::IPAddressSpace::kPublic) {
UseCounter::Count(frame->DomWindow(),
WebFeature::kMixedContentPrivateHostnameInPublicHostname);
// We can simplify the IP checks here, as we've already verified that
// |resourceIPAddress| is a reserved IP address, which means it's also a
// valid IP address in a normalized form.
if (resource_ip_address.StartsWith("127.0.0.") ||
resource_ip_address == "[::1]") {
UseCounter::Count(frame->DomWindow(),
frame->DomWindow()->IsSecureContext()
? WebFeature::kLoopbackEmbeddedInSecureContext
: WebFeature::kLoopbackEmbeddedInNonSecureContext);
}
LocalDOMWindow* window = frame->DomWindow();
if (!network::IsLessPublicAddressSpace(response.AddressSpace(),
window->AddressSpace())) {
return; // Not a private network request.
}
UseCounter::Count(window,
WebFeature::kMixedContentPrivateHostnameInPublicHostname);
if (response.AddressSpace() == network::mojom::IPAddressSpace::kLocal) {
UseCounter::Count(window,
window->IsSecureContext()
? WebFeature::kLoopbackEmbeddedInSecureContext
: WebFeature::kLoopbackEmbeddedInNonSecureContext);
}
}
......
......@@ -41,6 +41,7 @@
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/loader/fetch/https_state.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
#include "third_party/blink/renderer/platform/weborigin/reporting_disposition.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
......@@ -108,7 +109,7 @@ class CORE_EXPORT MixedContentChecker final {
const KURL& url);
static void CheckMixedPrivatePublic(LocalFrame*,
const AtomicString& resource_ip_address);
const ResourceResponse& response);
static WebMixedContentContextType ContextTypeForInspector(
LocalFrame*,
......
......@@ -160,22 +160,6 @@ void WorkerModuleScriptFetcher::NotifyClient(
kDoNotSupportReferrerPolicyLegacyKeywords, &response_referrer_policy);
}
// Calculate an address space from worker script's response url according to
// the "CORS and RFC1918" spec:
// https://wicg.github.io/cors-rfc1918/#integration-html
//
// Currently this implementation is not fully consistent with the spec for
// historical reasons.
// TODO(https://crbug.com/955213): Make this consistent with the spec.
// TODO(https://crbug.com/955213): Move this function to a more appropriate
// place so that this is shareable out of worker code.
auto response_address_space = network::mojom::IPAddressSpace::kPublic;
if (network_utils::IsReservedIPAddress(response.RemoteIPAddress())) {
response_address_space = network::mojom::IPAddressSpace::kPrivate;
}
if (SecurityOrigin::Create(response_url)->IsLocalhost())
response_address_space = network::mojom::IPAddressSpace::kLocal;
auto* response_content_security_policy =
MakeGarbageCollected<ContentSecurityPolicy>();
response_content_security_policy->DidReceiveHeaders(
......@@ -187,7 +171,7 @@ void WorkerModuleScriptFetcher::NotifyClient(
// Step 12.3-12.6 are implemented in Initialize().
global_scope_->Initialize(
response_url, response_referrer_policy, response_address_space,
response_url, response_referrer_policy, response.AddressSpace(),
response_content_security_policy->Headers(),
response_origin_trial_tokens.get(), response.AppCacheID());
}
......
......@@ -143,8 +143,7 @@ void ResourceLoadObserverForFrame::DidReceiveResponse(
return;
}
MixedContentChecker::CheckMixedPrivatePublic(frame,
response.RemoteIPAddress());
MixedContentChecker::CheckMixedPrivatePublic(frame, response);
std::unique_ptr<AlternateSignedExchangeResourceInfo> alternate_resource_info;
......
......@@ -242,19 +242,13 @@ void WorkerClassicScriptLoader::DidReceiveResponse(
response_url_ = response.ResponseUrl();
response_encoding_ = response.TextEncodingName();
app_cache_id_ = response.AppCacheID();
response_address_space_ = response.AddressSpace();
referrer_policy_ = response.HttpHeaderField(http_names::kReferrerPolicy);
ProcessContentSecurityPolicy(response);
origin_trial_tokens_ = OriginTrialContext::ParseHeaderValue(
response.HttpHeaderField(http_names::kOriginTrial));
if (network_utils::IsReservedIPAddress(response.RemoteIPAddress())) {
response_address_space_ =
SecurityOrigin::Create(response_url_)->IsLocalhost()
? network::mojom::IPAddressSpace::kLocal
: network::mojom::IPAddressSpace::kPrivate;
}
if (response_callback_)
std::move(response_callback_).Run();
}
......
......@@ -36,6 +36,7 @@
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "services/network/public/mojom/ip_address_space.mojom-shared.h"
#include "services/network/public/mojom/load_timing_info.mojom.h"
#include "third_party/blink/public/platform/web_http_header_visitor.h"
#include "third_party/blink/public/platform/web_http_load_info.h"
......@@ -428,6 +429,15 @@ void WebURLResponse::SetRemotePort(uint16_t remote_port) {
resource_response_->SetRemotePort(remote_port);
}
network::mojom::IPAddressSpace WebURLResponse::AddressSpace() const {
return resource_response_->AddressSpace();
}
void WebURLResponse::SetAddressSpace(
network::mojom::IPAddressSpace remote_ip_address_space) {
resource_response_->SetAddressSpace(remote_ip_address_space);
}
void WebURLResponse::SetEncodedDataLength(int64_t length) {
resource_response_->SetEncodedDataLength(length);
}
......
......@@ -35,6 +35,7 @@
#include "base/time/time.h"
#include "services/network/public/mojom/cross_origin_embedder_policy.mojom-shared.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "services/network/public/mojom/ip_address_space.mojom-shared.h"
#include "third_party/blink/public/platform/web_url_response.h"
#include "third_party/blink/renderer/platform/network/http_header_map.h"
#include "third_party/blink/renderer/platform/network/http_parsers.h"
......@@ -398,6 +399,11 @@ class PLATFORM_EXPORT ResourceResponse final {
uint16_t RemotePort() const { return remote_port_; }
void SetRemotePort(uint16_t value) { remote_port_ = value; }
network::mojom::IPAddressSpace AddressSpace() const { return address_space_; }
void SetAddressSpace(network::mojom::IPAddressSpace value) {
address_space_ = value;
}
bool WasAlpnNegotiated() const { return was_alpn_negotiated_; }
void SetWasAlpnNegotiated(bool was_alpn_negotiated) {
was_alpn_negotiated_ = was_alpn_negotiated;
......@@ -510,6 +516,10 @@ class PLATFORM_EXPORT ResourceResponse final {
// Remote port number of the socket which fetched this resource.
uint16_t remote_port_ = 0;
// The address space from which this resource was fetched.
network::mojom::IPAddressSpace address_space_ =
network::mojom::IPAddressSpace::kUnknown;
bool was_cached_ = false;
bool connection_reused_ = false;
bool is_null_ = false;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment