Cleanup cookies set by server redirects when FBD is enabled. (#27944)

This commit is contained in:
Aleksei Khoroshilov
2025-03-06 14:57:31 +07:00
committed by GitHub
parent b76149adcf
commit de5aa0daa8
4 changed files with 144 additions and 1 deletions
@@ -10,6 +10,7 @@
#include "base/memory/raw_ptr.h"
#include "base/path_service.h"
#include "base/strings/escape.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
@@ -41,7 +42,7 @@
#include "content/public/test/test_navigation_observer.h"
#include "net/base/features.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/default_handlers.h"
#include "net/http/http_status_code.h"
#include "net/test/embedded_test_server/http_response.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "services/network/public/cpp/network_switches.h"
@@ -106,6 +107,39 @@ std::unique_ptr<HttpResponse> HandleFileRequestWithCustomHeaders(
return http_response;
}
// This mostly mimics the behavior of /cross-site-with-cookie handler, but with
// additional cookie attributes set.
std::unique_ptr<HttpResponse> HandleCrossSiteRedirectWithSiteCookie(
EmbeddedTestServer* server,
const HttpRequest& request) {
const std::string prefix = "/cross-site-with-site-cookie";
if (!net::test_server::ShouldHandle(request, prefix)) {
return nullptr;
}
std::string dest_all = base::UnescapeBinaryURLComponent(
request.relative_url.substr(prefix.size() + 1));
std::string dest;
size_t delimiter = dest_all.find("/");
if (delimiter != std::string::npos) {
dest = base::StringPrintf(
"//%s:%hu/%s", dest_all.substr(0, delimiter).c_str(), server->port(),
dest_all.substr(delimiter + 1).c_str());
}
auto http_response = std::make_unique<BasicHttpResponse>();
http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
http_response->AddCustomHeader("Location", dest);
http_response->AddCustomHeader(
"Set-Cookie",
"server-redirect=true;path=/;SameSite=None;Secure;Max-Age=600");
http_response->set_content_type("text/html");
http_response->set_content(
base::StringPrintf("<!doctype html><p>Redirecting to %s", dest.c_str()));
return http_response;
}
class BrowsingDataRemoverObserver
: public content::BrowsingDataRemover::Observer {
public:
@@ -181,6 +215,8 @@ void EphemeralStorageBrowserTest::SetUpOnMainThread() {
base::PathService::Get(brave::DIR_TEST_DATA, &test_data_dirs[0]);
base::PathService::Get(content::DIR_TEST_DATA, &test_data_dirs[1]);
https_server_.RegisterRequestHandler(base::BindRepeating(
&HandleCrossSiteRedirectWithSiteCookie, &https_server_));
https_server_.RegisterDefaultHandler(base::BindRepeating(
&HandleFileRequestWithCustomHeaders,
base::Unretained(&http_request_monitor_), test_data_dirs));
@@ -271,6 +271,49 @@ IN_PROC_BROWSER_TEST_F(EphemeralStorageForgetByDefaultBrowserTest,
EXPECT_EQ("", values_site_b.iframe_2.cookies);
}
IN_PROC_BROWSER_TEST_F(EphemeralStorageForgetByDefaultBrowserTest,
NavigationRedirectCookiesAreCleared) {
brave_shields::SetForgetFirstPartyStorageEnabled(content_settings(), true,
GURL());
const GURL a_site_url =
https_server_.GetURL("0.com",
"/cross-site-with-site-cookie/1.com/"
"cross-site-with-site-cookie/a.com/empty.html");
WebContents* site_a = LoadURLInNewTab(a_site_url);
// Default cookie storage request should return non empty results for 0.com
// and 1.com as these websites should set cookies on server redirect.
EXPECT_FALSE(content::GetCookies(browser()->profile(),
https_server_.GetURL("0.com", "/"))
.empty());
EXPECT_FALSE(content::GetCookies(browser()->profile(),
https_server_.GetURL("1.com", "/"))
.empty());
// a.com/empty.html should not set cookies.
EXPECT_TRUE(content::GetCookies(browser()->profile(),
https_server_.GetURL("a.com", "/"))
.empty());
// Navigating to a new TLD should clear all ephemeral cookies after keep-alive
// timeout.
ASSERT_TRUE(content::NavigateToURL(site_a, c_site_ephemeral_storage_url_));
EXPECT_EQ(3u, WaitForCleanupAfterKeepAlive());
// Cookies should be cleared for 0.com and 1.com.
EXPECT_TRUE(content::GetCookies(browser()->profile(),
https_server_.GetURL("0.com", "/"))
.empty());
EXPECT_TRUE(content::GetCookies(browser()->profile(),
https_server_.GetURL("1.com", "/"))
.empty());
EXPECT_TRUE(content::GetCookies(browser()->profile(),
https_server_.GetURL("a.com", "/"))
.empty());
}
IN_PROC_BROWSER_TEST_F(EphemeralStorageForgetByDefaultBrowserTest,
PRE_ForgetFirstPartyAfterRestart) {
const GURL a_site_set_cookie_url(
@@ -60,6 +60,38 @@ EphemeralStorageTabHelper::GetEphemeralStorageToken(const url::Origin& origin) {
void EphemeralStorageTabHelper::WebContentsDestroyed() {}
void EphemeralStorageTabHelper::DidStartNavigation(
NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
navigation_handle->IsSameDocument()) {
return;
}
CreateProvisionalTLDEphemeralLifetime(navigation_handle);
}
void EphemeralStorageTabHelper::DidRedirectNavigation(
NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
navigation_handle->IsSameDocument()) {
return;
}
CreateProvisionalTLDEphemeralLifetime(navigation_handle);
}
void EphemeralStorageTabHelper::DidFinishNavigation(
NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame() ||
navigation_handle->IsSameDocument()) {
return;
}
// Clear all provisional ephemeral lifetimes. A committed ephemeral lifetime
// is created in ReadyToCommitNavigation().
provisional_tld_ephemeral_lifetimes_.clear();
}
void EphemeralStorageTabHelper::ReadyToCommitNavigation(
NavigationHandle* navigation_handle) {
if (!navigation_handle->IsInMainFrame()) {
@@ -96,6 +128,27 @@ void EphemeralStorageTabHelper::CreateEphemeralStorageAreasForDomainAndURL(
browser_context, new_domain, site_instance->GetStoragePartitionConfig());
}
void EphemeralStorageTabHelper::CreateProvisionalTLDEphemeralLifetime(
NavigationHandle* navigation_handle) {
const GURL& url = navigation_handle->GetURL();
if (!url.SchemeIsHTTPOrHTTPS()) {
return;
}
const std::string new_domain = net::URLToEphemeralStorageDomain(url);
if (new_domain.empty()) {
return;
}
auto* browser_context = web_contents()->GetBrowserContext();
auto* site_instance = web_contents()->GetSiteInstance();
provisional_tld_ephemeral_lifetimes_.emplace(
TLDEphemeralLifetime::GetOrCreate(
browser_context, new_domain,
site_instance->GetStoragePartitionConfig()));
}
void EphemeralStorageTabHelper::UpdateShieldsState(const GURL& url) {
if (!host_content_settings_map_ || !tld_ephemeral_lifetime_) {
return;
@@ -9,6 +9,7 @@
#include <optional>
#include <string>
#include "base/containers/flat_set.h"
#include "base/memory/weak_ptr.h"
#include "base/unguessable_token.h"
#include "brave/browser/ephemeral_storage/tld_ephemeral_lifetime.h"
@@ -45,10 +46,18 @@ class EphemeralStorageTabHelper
friend class content::WebContentsUserData<EphemeralStorageTabHelper>;
// WebContentsObserver
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override;
void DidRedirectNavigation(
content::NavigationHandle* navigation_handle) override;
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
void ReadyToCommitNavigation(
content::NavigationHandle* navigation_handle) override;
void WebContentsDestroyed() override;
void CreateProvisionalTLDEphemeralLifetime(
content::NavigationHandle* navigation_handle);
void CreateEphemeralStorageAreasForDomainAndURL(const std::string& new_domain,
const GURL& new_url);
@@ -57,6 +66,8 @@ class EphemeralStorageTabHelper
const base::raw_ptr<HostContentSettingsMap> host_content_settings_map_;
scoped_refptr<content_settings::CookieSettings> cookie_settings_;
scoped_refptr<content::SessionStorageNamespace> session_storage_namespace_;
base::flat_set<scoped_refptr<TLDEphemeralLifetime>>
provisional_tld_ephemeral_lifetimes_;
scoped_refptr<TLDEphemeralLifetime> tld_ephemeral_lifetime_;
base::WeakPtrFactory<EphemeralStorageTabHelper> weak_factory_{this};