305 lines
15 KiB
Diff
305 lines
15 KiB
Diff
From: uazo <uazo@users.noreply.github.com>
|
|
Date: Tue, 20 Sep 2022 07:20:01 +0000
|
|
Subject: Partition blobs by top frame URL
|
|
|
|
Verifies that the blob was created with the same top frame URL
|
|
or, if not defined, by the same agent cluster.
|
|
|
|
Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html
|
|
License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
|
|
---
|
|
.../renderer_host/render_frame_host_impl.cc | 2 +
|
|
.../Partition-blobs-by-top-frame-URL.inc | 1 +
|
|
storage/browser/blob/blob_url_store_impl.cc | 63 ++++++++++++++++++-
|
|
storage/browser/blob/blob_url_store_impl.h | 13 ++++
|
|
storage/browser/blob/features.cc | 1 +
|
|
.../public/mojom/blob/blob_url_store.mojom | 8 ++-
|
|
.../core/fileapi/public_url_manager.cc | 17 +++++
|
|
7 files changed, 101 insertions(+), 4 deletions(-)
|
|
create mode 100644 cromite_flags/third_party/blink/common/features_cc/Partition-blobs-by-top-frame-URL.inc
|
|
|
|
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
|
|
--- a/content/browser/renderer_host/render_frame_host_impl.cc
|
|
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
|
|
@@ -12782,6 +12782,8 @@ void RenderFrameHostImpl::ReportBlockingCrossPartitionBlobURL(
|
|
}
|
|
|
|
bool RenderFrameHostImpl::DoesDocumentHaveStorageAccess() {
|
|
+ // 6313600: [Blob URL] Allow contexts with a StorageAccessHandle
|
|
+ // to bypass Blob URL partitioning | https://chromium-review.googlesource.com/c/chromium/src/+/6313600
|
|
return GetContentClient()->browser()->IsFullCookieAccessAllowed(
|
|
GetBrowserContext(), WebContents::FromRenderFrameHost(this),
|
|
GetLastCommittedURL(), GetStorageKey(), GetCookieSettingOverrides());
|
|
diff --git a/cromite_flags/third_party/blink/common/features_cc/Partition-blobs-by-top-frame-URL.inc b/cromite_flags/third_party/blink/common/features_cc/Partition-blobs-by-top-frame-URL.inc
|
|
new file mode 100644
|
|
--- /dev/null
|
|
+++ b/cromite_flags/third_party/blink/common/features_cc/Partition-blobs-by-top-frame-URL.inc
|
|
@@ -0,0 +1 @@
|
|
+SET_CROMITE_FEATURE_ENABLED(kEnforceNoopenerOnBlobURLNavigation);
|
|
diff --git a/storage/browser/blob/blob_url_store_impl.cc b/storage/browser/blob/blob_url_store_impl.cc
|
|
--- a/storage/browser/blob/blob_url_store_impl.cc
|
|
+++ b/storage/browser/blob/blob_url_store_impl.cc
|
|
@@ -106,6 +106,30 @@ BlobURLStoreImpl::~BlobURLStoreImpl() {
|
|
}
|
|
}
|
|
|
|
+bool BlobURLStoreImpl::IsSamePartition(
|
|
+ const GURL& blob_url,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site) {
|
|
+ bool is_same_partition = false;
|
|
+ const std::optional<net::SchemefulSite>& top_level_site =
|
|
+ registry_->GetUnsafeTopLevelSite(blob_url);
|
|
+ const std::optional<base::UnguessableToken> agent_cluster_id =
|
|
+ registry_->GetUnsafeAgentClusterID(blob_url);
|
|
+ if (top_level_site.has_value()) {
|
|
+ is_same_partition = (top_level_site == unsafe_top_level_site);
|
|
+ } else {
|
|
+ is_same_partition = (agent_cluster_id == unsafe_agent_cluster_id);
|
|
+ }
|
|
+ // LOG(INFO) << "---BlobURLStoreImpl "
|
|
+ // << " is_same_partition=" << is_same_partition
|
|
+ // << " blob_url=" << blob_url
|
|
+ // << " top_level_site=" << (top_level_site.has_value() ? top_level_site->GetDebugString() : "<none>")
|
|
+ // << " unsafe_top_level_site=" << (unsafe_top_level_site.has_value() ? unsafe_top_level_site->GetDebugString() : "<none>")
|
|
+ // << " agent_cluster_id=" << (agent_cluster_id.has_value() ? agent_cluster_id->ToString() : "<none>")
|
|
+ // << " unsafe_agent_cluster_id=" << unsafe_agent_cluster_id.ToString();
|
|
+ return is_same_partition;
|
|
+}
|
|
+
|
|
void BlobURLStoreImpl::Register(
|
|
mojo::PendingRemote<blink::mojom::Blob> blob,
|
|
const GURL& url,
|
|
@@ -120,6 +144,14 @@ void BlobURLStoreImpl::Register(
|
|
return;
|
|
}
|
|
|
|
+ // LOG(INFO) << "---BlobURLStoreImpl Register"
|
|
+ // << " url=" << url
|
|
+ // << " storage_key_origin=" << storage_key_.GetDebugString()
|
|
+ // << " renderer_origin_=" << renderer_origin_
|
|
+ // << " render_process_host_id_=" << render_process_host_id_
|
|
+ // << " unsafe_top_level_site=" << (unsafe_top_level_site.has_value() ? unsafe_top_level_site->GetDebugString() : "<none>")
|
|
+ // << " unsafe_agent_cluster_id=" << unsafe_agent_cluster_id.ToString();
|
|
+
|
|
if (registry_)
|
|
registry_->AddUrlMapping(url, std::move(blob), storage_key_,
|
|
renderer_origin_, render_process_host_id_,
|
|
@@ -145,7 +177,7 @@ bool BlobURLStoreImpl::ShouldPartitionBlobUrlAccess(
|
|
features::kBlockCrossPartitionBlobUrlFetching) &&
|
|
!partitioning_disabled_by_policy_;
|
|
|
|
- const bool should_bypass_partitioning =
|
|
+ const bool should_bypass_partitioning = ((false)) &&
|
|
has_storage_access_handle &&
|
|
mapping_status ==
|
|
BlobUrlRegistry::MappingStatus::
|
|
@@ -156,26 +188,38 @@ bool BlobURLStoreImpl::ShouldPartitionBlobUrlAccess(
|
|
void BlobURLStoreImpl::ResolveAsURLLoaderFactory(
|
|
const GURL& url,
|
|
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
|
|
ResolveAsURLLoaderFactoryCallback callback) {
|
|
if (!registry_) {
|
|
BlobURLLoaderFactory::Create(mojo::NullRemote(), url, std::move(receiver));
|
|
std::move(callback).Run(std::nullopt, std::nullopt);
|
|
return;
|
|
}
|
|
- FinishResolveAsURLLoaderFactory(url, std::move(receiver), std::move(callback),
|
|
+ FinishResolveAsURLLoaderFactory(url, std::move(receiver),
|
|
+ unsafe_agent_cluster_id, unsafe_top_level_site,
|
|
+ std::move(callback),
|
|
storage_access_check_callback_.Run());
|
|
}
|
|
|
|
void BlobURLStoreImpl::FinishResolveAsURLLoaderFactory(
|
|
const GURL& url,
|
|
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
|
|
ResolveAsURLLoaderFactoryCallback callback,
|
|
bool has_storage_access_handle) {
|
|
+ if (!IsSamePartition(url, unsafe_agent_cluster_id, unsafe_top_level_site)) {
|
|
+ BlobURLLoaderFactory::Create(mojo::NullRemote(), url, std::move(receiver));
|
|
+ std::move(callback).Run(std::nullopt, std::nullopt);
|
|
+ return;
|
|
+ }
|
|
const BlobUrlRegistry::MappingStatus mapping_status =
|
|
registry_->IsUrlMapped(BlobUrlUtils::ClearUrlFragment(url), storage_key_);
|
|
if (IsBlobUrlAccessCrossPartitionSameOrigin(mapping_status)) {
|
|
if (ShouldPartitionBlobUrlAccess(has_storage_access_handle,
|
|
mapping_status)) {
|
|
+ // LOG(INFO) << "---ResolveAsURLLoaderFactory blocked" << url;
|
|
partitioning_blob_url_closure_.Run(url,
|
|
blink::mojom::PartitioningBlobURLInfo::
|
|
kBlockedCrossPartitionFetching);
|
|
@@ -187,6 +231,12 @@ void BlobURLStoreImpl::FinishResolveAsURLLoaderFactory(
|
|
partitioning_blob_url_closure_.Run(url, std::nullopt);
|
|
}
|
|
|
|
+ if (!IsSamePartition(url, unsafe_agent_cluster_id, unsafe_top_level_site)) {
|
|
+ BlobURLLoaderFactory::Create(mojo::NullRemote(), url, std::move(receiver));
|
|
+ std::move(callback).Run(std::nullopt, std::nullopt);
|
|
+ return;
|
|
+ }
|
|
+ // LOG(INFO) << "---ResolveAsURLLoaderFactory allowed " << url;
|
|
BlobURLLoaderFactory::Create(registry_->GetBlobFromUrl(url), url,
|
|
std::move(receiver));
|
|
// When a fragment URL is present, registry_->GetUnsafeAgentClusterID(url) and
|
|
@@ -201,6 +251,8 @@ void BlobURLStoreImpl::ResolveAsBlobURLToken(
|
|
const GURL& url,
|
|
mojo::PendingReceiver<blink::mojom::BlobURLToken> token,
|
|
bool is_top_level_navigation,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
|
|
ResolveAsBlobURLTokenCallback callback) {
|
|
// This function is known to be heap allocation heavy and performance
|
|
// critical. Extra memory safety checks can introduce regression
|
|
@@ -212,6 +264,7 @@ void BlobURLStoreImpl::ResolveAsBlobURLToken(
|
|
return;
|
|
}
|
|
FinishResolveAsBlobURLToken(url, std::move(token), is_top_level_navigation,
|
|
+ unsafe_agent_cluster_id, unsafe_top_level_site,
|
|
std::move(callback),
|
|
storage_access_check_callback_.Run());
|
|
}
|
|
@@ -220,8 +273,14 @@ void BlobURLStoreImpl::FinishResolveAsBlobURLToken(
|
|
const GURL& url,
|
|
mojo::PendingReceiver<blink::mojom::BlobURLToken> token,
|
|
bool is_top_level_navigation,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
|
|
ResolveAsBlobURLTokenCallback callback,
|
|
bool has_storage_access_handle) {
|
|
+ if (!IsSamePartition(url, unsafe_agent_cluster_id, unsafe_top_level_site)) {
|
|
+ std::move(callback).Run(std::nullopt);
|
|
+ return;
|
|
+ }
|
|
if (!is_top_level_navigation) {
|
|
const BlobUrlRegistry::MappingStatus mapping_status =
|
|
registry_->IsUrlMapped(BlobUrlUtils::ClearUrlFragment(url),
|
|
diff --git a/storage/browser/blob/blob_url_store_impl.h b/storage/browser/blob/blob_url_store_impl.h
|
|
--- a/storage/browser/blob/blob_url_store_impl.h
|
|
+++ b/storage/browser/blob/blob_url_store_impl.h
|
|
@@ -60,11 +60,15 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobURLStoreImpl
|
|
void ResolveAsURLLoaderFactory(
|
|
const GURL& url,
|
|
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
|
|
ResolveAsURLLoaderFactoryCallback callback) override;
|
|
void ResolveAsBlobURLToken(
|
|
const GURL& url,
|
|
mojo::PendingReceiver<blink::mojom::BlobURLToken> token,
|
|
bool is_top_level_navigation,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
|
|
ResolveAsBlobURLTokenCallback callback) override;
|
|
|
|
private:
|
|
@@ -81,6 +85,8 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobURLStoreImpl
|
|
void FinishResolveAsURLLoaderFactory(
|
|
const GURL& url,
|
|
mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
|
|
ResolveAsURLLoaderFactoryCallback callback,
|
|
bool has_storage_access_handle);
|
|
|
|
@@ -88,9 +94,16 @@ class COMPONENT_EXPORT(STORAGE_BROWSER) BlobURLStoreImpl
|
|
const GURL& url,
|
|
mojo::PendingReceiver<blink::mojom::BlobURLToken> token,
|
|
bool is_top_level_navigation,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site,
|
|
ResolveAsBlobURLTokenCallback callback,
|
|
bool has_storage_access_handle);
|
|
|
|
+ bool IsSamePartition(
|
|
+ const GURL& blob_url,
|
|
+ const base::UnguessableToken& unsafe_agent_cluster_id,
|
|
+ const std::optional<net::SchemefulSite>& unsafe_top_level_site);
|
|
+
|
|
const blink::StorageKey storage_key_;
|
|
// The origin used by the worker/document associated with this BlobURLStore on
|
|
// the renderer side. This will almost always be the same as `storage_key_`'s
|
|
diff --git a/storage/browser/blob/features.cc b/storage/browser/blob/features.cc
|
|
--- a/storage/browser/blob/features.cc
|
|
+++ b/storage/browser/blob/features.cc
|
|
@@ -18,6 +18,7 @@ BASE_FEATURE(kBlockCrossPartitionBlobUrlFetching,
|
|
#else
|
|
base::FEATURE_ENABLED_BY_DEFAULT);
|
|
#endif
|
|
+SET_CROMITE_FEATURE_ENABLED(kBlockCrossPartitionBlobUrlFetching);
|
|
|
|
// Please keep features in alphabetical order.
|
|
|
|
diff --git a/third_party/blink/public/mojom/blob/blob_url_store.mojom b/third_party/blink/public/mojom/blob/blob_url_store.mojom
|
|
--- a/third_party/blink/public/mojom/blob/blob_url_store.mojom
|
|
+++ b/third_party/blink/public/mojom/blob/blob_url_store.mojom
|
|
@@ -37,7 +37,9 @@ interface BlobURLStore {
|
|
// both the blob URL and all other references to the blob have been dropped.
|
|
ResolveAsURLLoaderFactory(
|
|
url.mojom.Url url,
|
|
- pending_receiver<network.mojom.URLLoaderFactory> factory) => (
|
|
+ pending_receiver<network.mojom.URLLoaderFactory> factory,
|
|
+ mojo_base.mojom.UnguessableToken unsafe_agent_cluster_id,
|
|
+ network.mojom.SchemefulSite? unsafe_top_level_site) => (
|
|
// TODO(https://crbug.com/1224926): Remove these once experiment is over.
|
|
mojo_base.mojom.UnguessableToken? unsafe_agent_cluster_id,
|
|
network.mojom.SchemefulSite? unsafe_top_level_site);
|
|
@@ -48,7 +50,9 @@ interface BlobURLStore {
|
|
// As long as the token is alive, the resolved blob will also be kept alive.
|
|
ResolveAsBlobURLToken(url.mojom.Url url,
|
|
pending_receiver<BlobURLToken> token,
|
|
- bool is_top_level_navigation) => (
|
|
+ bool is_top_level_navigation,
|
|
+ mojo_base.mojom.UnguessableToken unsafe_agent_cluster_id,
|
|
+ network.mojom.SchemefulSite? unsafe_top_level_site) => (
|
|
// TODO(https://crbug.com/1224926): Remove this once experiment is over.
|
|
mojo_base.mojom.UnguessableToken? unsafe_agent_cluster_id);
|
|
};
|
|
diff --git a/third_party/blink/renderer/core/fileapi/public_url_manager.cc b/third_party/blink/renderer/core/fileapi/public_url_manager.cc
|
|
--- a/third_party/blink/renderer/core/fileapi/public_url_manager.cc
|
|
+++ b/third_party/blink/renderer/core/fileapi/public_url_manager.cc
|
|
@@ -63,6 +63,21 @@ static void RemoveFromNullOriginMapIfNecessary(const KURL& blob_url) {
|
|
BlobURLNullOriginMap::GetInstance()->Remove(blob_url);
|
|
}
|
|
|
|
+static std::optional<BlinkSchemefulSite> GetInsecureTopLevelSite(
|
|
+ ExecutionContext* execution_context) {
|
|
+ std::optional<BlinkSchemefulSite> top_level_site;
|
|
+ if (execution_context->IsWindow()) {
|
|
+ auto* window = To<LocalDOMWindow>(execution_context);
|
|
+ if (window->top() && window->top()->GetFrame()) {
|
|
+ top_level_site = BlinkSchemefulSite(window->top()
|
|
+ ->GetFrame()
|
|
+ ->GetSecurityContext()
|
|
+ ->GetSecurityOrigin());
|
|
+ }
|
|
+ }
|
|
+ return top_level_site;
|
|
+}
|
|
+
|
|
} // namespace
|
|
|
|
PublicURLManager::PublicURLManager(ExecutionContext* execution_context)
|
|
@@ -269,6 +284,7 @@ void PublicURLManager::Resolve(
|
|
|
|
GetBlobURLStore().ResolveAsURLLoaderFactory(
|
|
url, std::move(factory_receiver),
|
|
+ GetExecutionContext()->GetAgentClusterID(), GetInsecureTopLevelSite(GetExecutionContext()),
|
|
WTF::BindOnce(metrics_callback, WrapPersistent(GetExecutionContext())));
|
|
}
|
|
|
|
@@ -293,6 +309,7 @@ void PublicURLManager::ResolveAsBlobURLToken(
|
|
|
|
GetBlobURLStore().ResolveAsBlobURLToken(
|
|
url, std::move(token_receiver), is_top_level_navigation,
|
|
+ GetExecutionContext()->GetAgentClusterID(), GetInsecureTopLevelSite(GetExecutionContext()),
|
|
WTF::BindOnce(metrics_callback, WrapPersistent(GetExecutionContext())));
|
|
}
|
|
|
|
--
|