From ae964a80daa0e7bbbab8ea7ae6430d0ae4d34164 Mon Sep 17 00:00:00 2001 From: Aleksey Khoroshilov Date: Tue, 17 Dec 2024 18:17:29 +0700 Subject: [PATCH] Prevent storage partitioning bypass with a trial when Shields active. --- browser/brave_content_browser_client.cc | 22 ++++ browser/brave_content_browser_client.h | 4 + .../ephemeral_storage_browsertest.cc | 124 ++++++++++++++++++ .../renderer_host/render_frame_host_impl.cc | 8 ++ .../public/browser/content_browser_client.cc | 6 + .../public/browser/content_browser_client.h | 2 + ...derer_host-render_frame_host_impl.cc.patch | 20 ++- 7 files changed, 184 insertions(+), 2 deletions(-) diff --git a/browser/brave_content_browser_client.cc b/browser/brave_content_browser_client.cc index 1b59ee88e47c..a855e1ee0e45 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -104,6 +104,7 @@ #include "chrome/browser/chrome_browser_interface_binders.h" #include "chrome/browser/chrome_browser_main.h" #include "chrome/browser/chrome_content_browser_client.h" +#include "chrome/browser/content_settings/cookie_settings_factory.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_io_data.h" @@ -689,6 +690,27 @@ BraveContentBrowserClient::GetEphemeralStorageToken( return es_tab_helper->GetEphemeralStorageToken(origin); } +bool BraveContentBrowserClient::CanThirdPartyStoragePartitioningBeDisabled( + content::BrowserContext* browser_context, + const url::Origin& origin) { + auto* host_content_settings_map = + HostContentSettingsMapFactory::GetForProfile(browser_context); + if (!host_content_settings_map) { + return false; + } + auto cookie_settings = CookieSettingsFactory::GetForProfile( + Profile::FromBrowserContext(browser_context)); + if (!cookie_settings) { + return false; + } + const auto url = origin.GetURL(); + return !brave_shields::GetBraveShieldsEnabled(host_content_settings_map, + url) || + brave_shields::GetCookieControlType(host_content_settings_map, + cookie_settings.get(), url) == + brave_shields::ControlType::ALLOW; +} + bool BraveContentBrowserClient::AllowWorkerFingerprinting( const GURL& url, content::BrowserContext* browser_context) { diff --git a/browser/brave_content_browser_client.h b/browser/brave_content_browser_client.h index 77165d5969e1..ef0e792002ac 100644 --- a/browser/brave_content_browser_client.h +++ b/browser/brave_content_browser_client.h @@ -74,6 +74,10 @@ class BraveContentBrowserClient : public ChromeContentBrowserClient { content::RenderFrameHost* render_frame_host, const url::Origin& origin) override; + bool CanThirdPartyStoragePartitioningBeDisabled( + content::BrowserContext* browser_context, + const url::Origin& origin) override; + bool AllowWorkerFingerprinting( const GURL& url, content::BrowserContext* browser_context) override; diff --git a/browser/ephemeral_storage/ephemeral_storage_browsertest.cc b/browser/ephemeral_storage/ephemeral_storage_browsertest.cc index 916fb7ccb046..f30f15739623 100644 --- a/browser/ephemeral_storage/ephemeral_storage_browsertest.cc +++ b/browser/ephemeral_storage/ephemeral_storage_browsertest.cc @@ -26,12 +26,15 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_enums.h" +#include "chrome/test/base/chrome_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/prefs/pref_service.h" #include "content/public/browser/browsing_data_remover.h" +#include "content/public/browser/navigation_handle.h" #include "content/public/browser/storage_partition.h" +#include "content/public/browser/web_contents_observer.h" #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/test/browser_test.h" @@ -1277,3 +1280,124 @@ IN_PROC_BROWSER_TEST_F( EXPECT_EQ("", values_after.iframe_1.cookies); EXPECT_EQ("", values_after.iframe_2.cookies); } + +class EphemeralStorageWithDisableThirdPartyStoragePartitioningBrowserTest + : public EphemeralStorageBrowserTest, + public content::WebContentsObserver, + public testing::WithParamInterface { + public: + EphemeralStorageWithDisableThirdPartyStoragePartitioningBrowserTest() {} + + void ReadyToCommitNavigation( + content::NavigationHandle* navigation_handle) override { + if (!navigation_handle->IsInPrimaryMainFrame()) { + return; + } + + blink::RuntimeFeatureStateContext& context = + navigation_handle->GetMutableRuntimeFeatureStateContext(); + context.SetDisableThirdPartyStoragePartitioning2Enabled(GetParam()); + } +}; + +IN_PROC_BROWSER_TEST_P( + EphemeralStorageWithDisableThirdPartyStoragePartitioningBrowserTest, + StorageIsPartitionedWithShieldsEnabled) { + auto* web_contents = chrome_test_utils::GetActiveWebContents(this); + Observe(web_contents); + ASSERT_TRUE( + brave_shields::GetBraveShieldsEnabled(content_settings(), GURL())); + + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_)); + + // The page this tab is loaded via a.com and has two b.com third-party + // iframes. The third-party iframes should be partitioned. + SetValuesInFrames(web_contents, "a.com", "from=a.com"); + ValuesFromFrames third_party_values = GetValuesFromFrames(web_contents); + EXPECT_EQ("a.com", third_party_values.main_frame.local_storage); + EXPECT_EQ("a.com", third_party_values.iframe_1.local_storage); + EXPECT_EQ("a.com", third_party_values.iframe_2.local_storage); + + EXPECT_EQ("a.com", third_party_values.main_frame.session_storage); + EXPECT_EQ("a.com", third_party_values.iframe_1.session_storage); + EXPECT_EQ("a.com", third_party_values.iframe_2.session_storage); + + EXPECT_EQ("from=a.com", third_party_values.main_frame.cookies); + EXPECT_EQ("from=a.com", third_party_values.iframe_1.cookies); + EXPECT_EQ("from=a.com", third_party_values.iframe_2.cookies); + + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), b_site_ephemeral_storage_url_)); + + // The storage in the first-party iframes should reflect empty values not + // altered by a.com site. + ValuesFromFrames first_party_values = GetValuesFromFrames(web_contents); + ExpectValuesFromFramesAreEmpty(FROM_HERE, first_party_values); +} + +IN_PROC_BROWSER_TEST_P( + EphemeralStorageWithDisableThirdPartyStoragePartitioningBrowserTest, + StorageIsPartitionedWithShieldsDisabled) { + auto* web_contents = chrome_test_utils::GetActiveWebContents(this); + Observe(chrome_test_utils::GetActiveWebContents(this)); + brave_shields::SetBraveShieldsEnabled(content_settings(), false, + a_site_ephemeral_storage_url_); + brave_shields::SetBraveShieldsEnabled(content_settings(), false, + b_site_ephemeral_storage_url_); + brave_shields::SetBraveShieldsEnabled(content_settings(), false, + c_site_ephemeral_storage_url_); + + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), a_site_ephemeral_storage_url_)); + + // The page this tab is loaded via a.com and has two b.com third-party + // iframes. The third-party iframes should be partitioned. + SetValuesInFrames(web_contents, "a.com", "from=a.com"); + ValuesFromFrames third_party_values = GetValuesFromFrames(web_contents); + EXPECT_EQ("a.com", third_party_values.main_frame.local_storage); + EXPECT_EQ("a.com", third_party_values.iframe_1.local_storage); + EXPECT_EQ("a.com", third_party_values.iframe_2.local_storage); + + EXPECT_EQ("a.com", third_party_values.main_frame.session_storage); + EXPECT_EQ("a.com", third_party_values.iframe_1.session_storage); + EXPECT_EQ("a.com", third_party_values.iframe_2.session_storage); + + EXPECT_EQ("from=a.com", third_party_values.main_frame.cookies); + EXPECT_EQ("from=a.com", third_party_values.iframe_1.cookies); + EXPECT_EQ("from=a.com", third_party_values.iframe_2.cookies); + + ASSERT_TRUE( + ui_test_utils::NavigateToURL(browser(), b_site_ephemeral_storage_url_)); + + ValuesFromFrames first_party_values = GetValuesFromFrames(web_contents); + if (GetParam()) { + // If origin trial is enabled, the storage should not be partitioned. + EXPECT_EQ("a.com", first_party_values.main_frame.local_storage); + EXPECT_EQ("a.com", first_party_values.iframe_1.local_storage); + EXPECT_EQ("a.com", first_party_values.iframe_2.local_storage); + + EXPECT_EQ("a.com", first_party_values.main_frame.session_storage); + EXPECT_EQ("a.com", first_party_values.iframe_1.session_storage); + EXPECT_EQ("a.com", first_party_values.iframe_2.session_storage); + } else { + // If origin trial is disabled, the storage should be partitioned. + EXPECT_EQ(nullptr, first_party_values.main_frame.local_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_1.local_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_2.local_storage); + + EXPECT_EQ(nullptr, first_party_values.main_frame.session_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_1.session_storage); + EXPECT_EQ(nullptr, first_party_values.iframe_2.session_storage); + } + + // Cookies are not partitioned if shields are disabled. + EXPECT_EQ("from=a.com", first_party_values.main_frame.cookies); + EXPECT_EQ("from=a.com", first_party_values.iframe_1.cookies); + EXPECT_EQ("from=a.com", first_party_values.iframe_2.cookies); +} + +INSTANTIATE_TEST_SUITE_P( + , + EphemeralStorageWithDisableThirdPartyStoragePartitioningBrowserTest, + testing::Bool()); diff --git a/chromium_src/content/browser/renderer_host/render_frame_host_impl.cc b/chromium_src/content/browser/renderer_host/render_frame_host_impl.cc index cb80730f2708..6e059166f0a4 100644 --- a/chromium_src/content/browser/renderer_host/render_frame_host_impl.cc +++ b/chromium_src/content/browser/renderer_host/render_frame_host_impl.cc @@ -13,10 +13,18 @@ return *ephemeral_storage_token; \ } +#define BRAVE_RENDER_FRAME_HOST_IMPL_IS_THIRD_PARTY_STORAGE_PARTITIONING_ENABLED_CHECK_IF_CAN_BE_DISABLED \ + if (GetContentClient() \ + ->browser() \ + ->CanThirdPartyStoragePartitioningBeDisabled( \ + GetBrowserContext(), \ + main_frame_for_storage_partitioning->GetLastCommittedOrigin())) + #include "src/content/browser/renderer_host/render_frame_host_impl.cc" #undef BRAVE_RENDER_FRAME_HOST_IMPL_COMPUTE_ISOLATION_INFO_INTERNAL #undef BRAVE_RENDER_FRAME_HOST_IMPL_COMPUTE_NONCE +#undef BRAVE_RENDER_FRAME_HOST_IMPL_IS_THIRD_PARTY_STORAGE_PARTITIONING_ENABLED_CHECK_IF_CAN_BE_DISABLED namespace content { diff --git a/chromium_src/content/public/browser/content_browser_client.cc b/chromium_src/content/public/browser/content_browser_client.cc index 6f7abb93b101..cfab26aa0856 100644 --- a/chromium_src/content/public/browser/content_browser_client.cc +++ b/chromium_src/content/public/browser/content_browser_client.cc @@ -28,6 +28,12 @@ ContentBrowserClient::GetEphemeralStorageToken( return std::nullopt; } +bool ContentBrowserClient::CanThirdPartyStoragePartitioningBeDisabled( + BrowserContext* browser_context, + const url::Origin& origin) { + return false; +} + brave_shields::mojom::ShieldsSettingsPtr ContentBrowserClient::WorkerGetBraveShieldSettings( const GURL& url, diff --git a/chromium_src/content/public/browser/content_browser_client.h b/chromium_src/content/public/browser/content_browser_client.h index d8e514a930bb..e775021d0b64 100644 --- a/chromium_src/content/public/browser/content_browser_client.h +++ b/chromium_src/content/public/browser/content_browser_client.h @@ -24,6 +24,8 @@ const GURL& url); \ virtual std::optional GetEphemeralStorageToken( \ RenderFrameHost* render_frame_host, const url::Origin& origin); \ + virtual bool CanThirdPartyStoragePartitioningBeDisabled( \ + BrowserContext* browser_context, const url::Origin& origin); \ virtual bool AllowWorkerFingerprinting(const GURL& url, \ BrowserContext* browser_context); \ virtual brave_shields::mojom::ShieldsSettingsPtr \ diff --git a/patches/content-browser-renderer_host-render_frame_host_impl.cc.patch b/patches/content-browser-renderer_host-render_frame_host_impl.cc.patch index b02ad172fa69..40eea644c724 100644 --- a/patches/content-browser-renderer_host-render_frame_host_impl.cc.patch +++ b/patches/content-browser-renderer_host-render_frame_host_impl.cc.patch @@ -1,5 +1,5 @@ diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc -index f58b9b0333e01f95ecf1b1d3fd3e273b675705cc..0d8c54fa8dcf8052f9c6d6a28cd5d0395688ec1e 100644 +index f58b9b0333e01f95ecf1b1d3fd3e273b675705cc..e14627f84f7dab5f368e36dbc30d8fbeb87409df 100644 --- a/content/browser/renderer_host/render_frame_host_impl.cc +++ b/content/browser/renderer_host/render_frame_host_impl.cc @@ -4820,6 +4820,7 @@ net::IsolationInfo RenderFrameHostImpl::ComputeIsolationInfoInternal( @@ -18,7 +18,23 @@ index f58b9b0333e01f95ecf1b1d3fd3e273b675705cc..0d8c54fa8dcf8052f9c6d6a28cd5d039 // If it's a credentialless frame tree, use its nonce even if it's within a // fenced frame tree to maintain the guarantee that a credentialless frame // tree has a unique nonce. -@@ -9236,6 +9238,7 @@ void RenderFrameHostImpl::CreateNewWindow( +@@ -4872,6 +4874,7 @@ bool RenderFrameHostImpl::IsThirdPartyStoragePartitioningEnabled( + // current value of net::features::ThirdPartyStoragePartitioning. + if (rfs_document_data_for_storage_key->runtime_feature_state_read_context() + .IsDisableThirdPartyStoragePartitioning2Enabled()) { ++ BRAVE_RENDER_FRAME_HOST_IMPL_IS_THIRD_PARTY_STORAGE_PARTITIONING_ENABLED_CHECK_IF_CAN_BE_DISABLED + return false; + } + // Compile the list of third-party origins we need to check in addition to +@@ -4889,6 +4892,7 @@ bool RenderFrameHostImpl::IsThirdPartyStoragePartitioningEnabled( + if (rfs_document_data_for_storage_key->runtime_feature_state_read_context() + .IsDisableThirdPartyStoragePartitioning2EnabledForThirdParty( + third_party_origins)) { ++ BRAVE_RENDER_FRAME_HOST_IMPL_IS_THIRD_PARTY_STORAGE_PARTITIONING_ENABLED_CHECK_IF_CAN_BE_DISABLED + return false; + } + } else { +@@ -9236,6 +9240,7 @@ void RenderFrameHostImpl::CreateNewWindow( dom_storage_context, params->session_storage_namespace_id); }