From 76bd8669c9bd6147a6deeb13e660a5c13c835907 Mon Sep 17 00:00:00 2001 From: Mark Pilgrim Date: Thu, 9 Jul 2020 11:57:03 -0400 Subject: [PATCH] Implement navigator.userAgent farbling . . . . . . . . . . . . fix unused function error on Linux break out build, always set UA, test farbling reset build things more deps less nesting no nesting don't run on DidRedirectNavigation, explicitly set ua override to false if we're not overriding max UA should be suffixed off default UA, not upstream UA --- browser/BUILD.gn | 1 + browser/brave_tab_helpers.cc | 3 + browser/farbling/BUILD.gn | 18 ++ ...avigator_useragent_farbling_browsertest.cc | 161 +++++++++++++++++ browser/farbling/farbling_tab_helper.cc | 162 ++++++++++++++++++ browser/farbling/farbling_tab_helper.h | 43 +++++ .../browser/network_session_configurator.cc | 27 +++ .../blink/renderer/core/frame/navigator.cc | 43 +++++ ...ink-renderer-core-frame-navigator.cc.patch | 12 ++ test/BUILD.gn | 1 + 10 files changed, 471 insertions(+) create mode 100644 browser/farbling/BUILD.gn create mode 100644 browser/farbling/brave_navigator_useragent_farbling_browsertest.cc create mode 100644 browser/farbling/farbling_tab_helper.cc create mode 100644 browser/farbling/farbling_tab_helper.h create mode 100644 chromium_src/components/network_session_configurator/browser/network_session_configurator.cc create mode 100644 chromium_src/third_party/blink/renderer/core/frame/navigator.cc create mode 100644 patches/third_party-blink-renderer-core-frame-navigator.cc.patch diff --git a/browser/BUILD.gn b/browser/BUILD.gn index 66acd8ab9b9c..145b264d923c 100644 --- a/browser/BUILD.gn +++ b/browser/BUILD.gn @@ -111,6 +111,7 @@ source_set("browser_process") { "autoplay", "content_settings", "download", + "farbling", "net", "profiles", "renderer_context_menu", diff --git a/browser/brave_tab_helpers.cc b/browser/brave_tab_helpers.cc index a4b82848cf64..6617dd99bb1a 100644 --- a/browser/brave_tab_helpers.cc +++ b/browser/brave_tab_helpers.cc @@ -7,6 +7,7 @@ #include "base/command_line.h" #include "base/feature_list.h" +#include "brave/browser/farbling/farbling_tab_helper.h" #include "brave/browser/tor/buildflags.h" #include "brave/browser/ui/bookmark/brave_bookmark_tab_helper.h" #include "brave/common/brave_switches.h" @@ -123,6 +124,8 @@ void AttachTabHelpers(content::WebContents* web_contents) { ipfs::IPFSTabHelper::CreateForWebContents(web_contents); } #endif + + FarblingTabHelper::CreateForWebContents(web_contents); } } // namespace brave diff --git a/browser/farbling/BUILD.gn b/browser/farbling/BUILD.gn new file mode 100644 index 000000000000..dc65c8fa0cb2 --- /dev/null +++ b/browser/farbling/BUILD.gn @@ -0,0 +1,18 @@ +source_set("farbling") { + check_includes = false + sources = [ + "farbling_tab_helper.cc", + "farbling_tab_helper.h", + ] + + deps = [ + "//base", + "//brave/components/brave_shields/browser:browser", + "//components/version_info", + "//content/public/browser", + "//content/public/common", + "//chrome/common", + "//net", + "//third_party/blink/public/common", + ] +} diff --git a/browser/farbling/brave_navigator_useragent_farbling_browsertest.cc b/browser/farbling/brave_navigator_useragent_farbling_browsertest.cc new file mode 100644 index 000000000000..80244a128fb9 --- /dev/null +++ b/browser/farbling/brave_navigator_useragent_farbling_browsertest.cc @@ -0,0 +1,161 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "base/path_service.h" +#include "base/strings/stringprintf.h" +#include "base/task/post_task.h" +#include "base/test/thread_test_helper.h" +#include "brave/browser/brave_browser_process_impl.h" +#include "brave/browser/brave_content_browser_client.h" +#include "brave/browser/extensions/brave_base_local_data_files_browsertest.h" +#include "brave/common/brave_paths.h" +#include "brave/common/pref_names.h" +#include "brave/components/brave_component_updater/browser/local_data_files_service.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/extensions/extension_browsertest.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_content_client.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/permissions/permission_request.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" + +using brave_shields::ControlType; + +const char kUserAgentScript[] = + "domAutomationController.send(navigator.userAgent);"; + +class BraveNavigatorUserAgentFarblingBrowserTest : public InProcessBrowserTest { + public: + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + + content_client_.reset(new ChromeContentClient); + content::SetContentClient(content_client_.get()); + browser_content_client_.reset(new BraveContentBrowserClient()); + content::SetBrowserClientForTesting(browser_content_client_.get()); + + host_resolver()->AddRule("*", "127.0.0.1"); + content::SetupCrossSiteRedirector(embedded_test_server()); + + brave::RegisterPathProvider(); + base::FilePath test_data_dir; + base::PathService::Get(brave::DIR_TEST_DATA, &test_data_dir); + embedded_test_server()->ServeFilesFromDirectory(test_data_dir); + + ASSERT_TRUE(embedded_test_server()->Start()); + } + + void TearDown() override { + browser_content_client_.reset(); + content_client_.reset(); + } + + HostContentSettingsMap* content_settings() { + return HostContentSettingsMapFactory::GetForProfile(browser()->profile()); + } + + void AllowFingerprinting(std::string domain) { + brave_shields::SetFingerprintingControlType( + content_settings(), ControlType::ALLOW, + embedded_test_server()->GetURL(domain, "/")); + } + + void BlockFingerprinting(std::string domain) { + brave_shields::SetFingerprintingControlType( + content_settings(), ControlType::BLOCK, + embedded_test_server()->GetURL(domain, "/")); + } + + void SetFingerprintingDefault(std::string domain) { + brave_shields::SetFingerprintingControlType( + content_settings(), ControlType::DEFAULT, + embedded_test_server()->GetURL(domain, "/")); + } + + template + int ExecScriptGetInt(const std::string& script, T* frame) { + int value; + EXPECT_TRUE(ExecuteScriptAndExtractInt(frame, script, &value)); + return value; + } + + template + std::string ExecScriptGetStr(const std::string& script, T* frame) { + std::string value; + EXPECT_TRUE(ExecuteScriptAndExtractString(frame, script, &value)); + return value; + } + + content::WebContents* contents() { + return browser()->tab_strip_model()->GetActiveWebContents(); + } + + bool NavigateToURLUntilLoadStop(const GURL& url) { + ui_test_utils::NavigateToURL(browser(), url); + return WaitForLoadStop(contents()); + } + + private: + std::unique_ptr content_client_; + std::unique_ptr browser_content_client_; +}; + +// Tests results of farbling user agent +IN_PROC_BROWSER_TEST_F(BraveNavigatorUserAgentFarblingBrowserTest, + FarbleNavigatorUserAgent) { + std::string domain_b = "b.com"; + std::string domain_z = "z.com"; + GURL url_b = embedded_test_server()->GetURL(domain_b, "/simple.html"); + GURL url_z = embedded_test_server()->GetURL(domain_z, "/simple.html"); + + // Farbling level: off + // get real navigator.userAgent + AllowFingerprinting(domain_b); + NavigateToURLUntilLoadStop(url_b); + std::string off_ua_b = ExecScriptGetStr(kUserAgentScript, contents()); + AllowFingerprinting(domain_z); + NavigateToURLUntilLoadStop(url_z); + std::string off_ua_z = ExecScriptGetStr(kUserAgentScript, contents()); + // user agent should be the same on every domain if farbling is off + EXPECT_EQ(off_ua_b, off_ua_z); + + // Farbling level: default + // navigator.userAgent may be farbled, but the farbling is not + // domain-specific + SetFingerprintingDefault(domain_b); + NavigateToURLUntilLoadStop(url_b); + std::string default_ua_b = ExecScriptGetStr(kUserAgentScript, contents()); + SetFingerprintingDefault(domain_z); + NavigateToURLUntilLoadStop(url_z); + std::string default_ua_z = ExecScriptGetStr(kUserAgentScript, contents()); + // user agent should be the same on every domain if farbling is default + EXPECT_EQ(default_ua_b, default_ua_z); + + // Farbling level: maximum + // navigator.userAgent should be the possibly-farbled string from the default + // farbling level, further suffixed by a pseudo-random number of spaces based + // on domain and session key + BlockFingerprinting(domain_b); + NavigateToURLUntilLoadStop(url_b); + std::string max_ua_b = ExecScriptGetStr(kUserAgentScript, contents()); + EXPECT_EQ(max_ua_b, default_ua_b + " "); + BlockFingerprinting(domain_z); + NavigateToURLUntilLoadStop(url_z); + std::string max_ua_z = ExecScriptGetStr(kUserAgentScript, contents()); + EXPECT_EQ(max_ua_z, default_ua_z + " "); + + // Farbling level: off + // verify that user agent is reset properly after having been farbled + AllowFingerprinting(domain_b); + NavigateToURLUntilLoadStop(url_b); + std::string off_ua_b2 = ExecScriptGetStr(kUserAgentScript, contents()); + EXPECT_EQ(off_ua_b, off_ua_b2); +} diff --git a/browser/farbling/farbling_tab_helper.cc b/browser/farbling/farbling_tab_helper.cc new file mode 100644 index 000000000000..1f79140dad96 --- /dev/null +++ b/browser/farbling/farbling_tab_helper.cc @@ -0,0 +1,162 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "brave/browser/farbling/farbling_tab_helper.h" + +#include +#include + +#include "base/system/sys_info.h" +#include "brave/components/brave_shields/browser/brave_shields_util.h" +#include "chrome/browser/chrome_content_browser_client.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "components/version_info/version_info.h" +#include "content/public/browser/navigation_handle.h" +#include "content/public/common/content_client.h" +#include "content/public/common/user_agent.h" +#include "net/http/http_util.h" +#include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/common/user_agent/user_agent_metadata.h" + +using brave_shields::ControlType; +using brave_shields::GetBraveShieldsEnabled; +using brave_shields::GetFingerprintingControlType; + +namespace { + +#if defined(OS_MACOSX) +int32_t GetMinimumBugfixVersion(int32_t os_major_version, + int32_t os_minor_version) { + if (os_major_version == 10) { + switch (os_minor_version) { + case 9: + case 10: + return 5; + case 11: + case 12: + case 13: + case 14: + case 15: + return 6; + } + } + return 0; +} +#endif + +std::string GetUserAgentPlatform() { +#if defined(OS_WIN) + return ""; +#elif defined(OS_MACOSX) + return "Macintosh; "; +#elif defined(USE_X11) + return "X11; "; +#elif defined(OS_ANDROID) + return "Linux; "; +#elif defined(OS_POSIX) + return "Unknown; "; +#endif +} + +std::string GetMinimalProduct() { + return version_info::GetProductNameAndVersionForUserAgent(); +} + +std::string GetMinimalOSVersion() { + std::string os_version; +#if defined(OS_WIN) || defined(OS_MACOSX) + int32_t os_major_version = 0; + int32_t os_minor_version = 0; + int32_t os_bugfix_version = 0; + base::SysInfo::OperatingSystemVersionNumbers( + &os_major_version, &os_minor_version, &os_bugfix_version); +#endif +#if defined(OS_MACOSX) + os_bugfix_version = + std::max(os_bugfix_version, + GetMinimumBugfixVersion(os_major_version, os_minor_version)); +#endif + +#if defined(OS_ANDROID) + std::string android_version_str = base::SysInfo::OperatingSystemVersion(); + std::string android_info_str = + GetAndroidOSInfo(content::IncludeAndroidBuildNumber::Exclude, + content::IncludeAndroidModel::Exclude); +#endif + + base::StringAppendF(&os_version, +#if defined(OS_WIN) + "%d.%d", os_major_version, os_minor_version +#elif defined(OS_MACOSX) + "%d_%d_%d", os_major_version, os_minor_version, + os_bugfix_version +#elif defined(OS_ANDROID) + "%s%s", android_version_str.c_str(), + android_info_str.c_str() +#else + "" +#endif + ); // NOLINT + return os_version; +} + +} // namespace + +namespace brave { + +FarblingTabHelper::~FarblingTabHelper() = default; + +FarblingTabHelper::FarblingTabHelper(content::WebContents* web_contents) + : content::WebContentsObserver(web_contents) {} + +void FarblingTabHelper::DidStartNavigation( + content::NavigationHandle* navigation_handle) { + UpdateUserAgent(navigation_handle); +} + +void FarblingTabHelper::UpdateUserAgent( + content::NavigationHandle* navigation_handle) { + if (!navigation_handle) + return; + content::WebContents* web_contents = navigation_handle->GetWebContents(); + if (!web_contents) + return; + std::string ua = ""; + Profile* profile = static_cast(web_contents->GetBrowserContext()); + auto* map = HostContentSettingsMapFactory::GetForProfile(profile); + // If shields is off or farbling is off, do not override. + // Also, we construct real user agent two different ways, through the browser + // client's higher level utility function and through direct functions. If + // they differ, there's some sort of override happening. Maybe the end + // user is forcing the user agent via command line flags. Or maybe they + // turned on the "freeze user agent" flag. Whatever it is, we want to + // respect it. + if (GetBraveShieldsEnabled(map, navigation_handle->GetURL()) && + (GetFingerprintingControlType(map, navigation_handle->GetURL()) != + ControlType::ALLOW) && + (GetUserAgent() == + content::BuildUserAgentFromProduct( + version_info::GetProductNameAndVersionForUserAgent()))) { + std::string minimal_os_info; + base::StringAppendF(&minimal_os_info, "%s%s", + GetUserAgentPlatform().c_str(), + content::BuildOSCpuInfoFromOSVersionAndCpuType( + GetMinimalOSVersion(), content::BuildCpuInfo()) + .c_str()); + ua = content::BuildUserAgentFromOSAndProduct(minimal_os_info, + GetMinimalProduct()); + navigation_handle->SetIsOverridingUserAgent(true); + web_contents->SetUserAgentOverride( + blink::UserAgentOverride::UserAgentOnly(ua), + false /* override_in_new_tabs */); + } else { + navigation_handle->SetIsOverridingUserAgent(false); + } +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(FarblingTabHelper) + +} // namespace brave diff --git a/browser/farbling/farbling_tab_helper.h b/browser/farbling/farbling_tab_helper.h new file mode 100644 index 000000000000..1d38e7b9eb4b --- /dev/null +++ b/browser/farbling/farbling_tab_helper.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BRAVE_BROWSER_FARBLING_FARBLING_TAB_HELPER_H_ +#define BRAVE_BROWSER_FARBLING_FARBLING_TAB_HELPER_H_ + +#include "content/public/browser/web_contents_observer.h" +#include "content/public/browser/web_contents_user_data.h" + +namespace content { +class NavigationHandle; +class WebContents; +} // namespace content + +namespace brave { + +class FarblingTabHelper + : public content::WebContentsObserver, + public content::WebContentsUserData { + public: + ~FarblingTabHelper() override; + + FarblingTabHelper(const FarblingTabHelper&) = delete; + FarblingTabHelper& operator=(FarblingTabHelper&) = delete; + + private: + friend class content::WebContentsUserData; + explicit FarblingTabHelper(content::WebContents* web_contents); + + void UpdateUserAgent(content::NavigationHandle* navigation_handle); + + // content::WebContentsObserver + void DidStartNavigation( + content::NavigationHandle* navigation_handle) override; + + WEB_CONTENTS_USER_DATA_KEY_DECL(); +}; + +} // namespace brave + +#endif // BRAVE_BROWSER_FARBLING_FARBLING_TAB_HELPER_H_ diff --git a/chromium_src/components/network_session_configurator/browser/network_session_configurator.cc b/chromium_src/components/network_session_configurator/browser/network_session_configurator.cc new file mode 100644 index 000000000000..ea02afccdcef --- /dev/null +++ b/chromium_src/components/network_session_configurator/browser/network_session_configurator.cc @@ -0,0 +1,27 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#define ParseCommandLineAndFieldTrials \ + ParseCommandLineAndFieldTrials_ChromiumImpl +#include "../../../../../components/network_session_configurator/browser/network_session_configurator.cc" +#undef ParseCommandLineAndFieldTrials + +namespace network_session_configurator { + +// Never send QUIC user agent. +void ParseCommandLineAndFieldTrials(const base::CommandLine& command_line, + bool is_quic_force_disabled, + const std::string& quic_user_agent_id, + net::HttpNetworkSession::Params* params, + net::QuicParams* quic_params) { + ParseCommandLineAndFieldTrials_ChromiumImpl( + command_line, + is_quic_force_disabled, + "" /* quic_user_agent_id */, + params, + quic_params); +} + +} // namespace network_session_configurator diff --git a/chromium_src/third_party/blink/renderer/core/frame/navigator.cc b/chromium_src/third_party/blink/renderer/core/frame/navigator.cc new file mode 100644 index 000000000000..5f902cb9aa07 --- /dev/null +++ b/chromium_src/third_party/blink/renderer/core/frame/navigator.cc @@ -0,0 +1,43 @@ +/* Copyright (c) 2020 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include "brave/components/content_settings/renderer/brave_content_settings_agent_impl_helper.h" +#include "third_party/blink/public/platform/web_content_settings_client.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/loader/frame_loader.h" +#include "third_party/blink/renderer/platform/wtf/text/string_builder.h" + +using blink::LocalFrame; +using WTF::String; +using WTF::StringBuilder; + +namespace brave { + +const int kFarbledUserAgentMaxExtraSpaces = 5; + +String FarbledUserAgent(LocalFrame* frame, std::mt19937_64 prng) { + StringBuilder result; + result.Append(frame->Loader().UserAgent()); + int extra = prng() % kFarbledUserAgentMaxExtraSpaces; + for (int i = 0; i < extra; i++) + result.Append(" "); + return result.ToString(); +} + +} // namespace brave + +#define BRAVE_NAVIGATOR_USERAGENT \ + if (!AllowFingerprinting(GetFrame())) \ + return brave::FarbledUserAgent( \ + GetFrame(), \ + brave::BraveSessionCache::From(*(GetFrame()->GetDocument())) \ + .MakePseudoRandomGenerator()); + +#include "../../../../../../../third_party/blink/renderer/core/frame/navigator.cc" + +#undef BRAVE_NAVIGATOR_USERAGENT diff --git a/patches/third_party-blink-renderer-core-frame-navigator.cc.patch b/patches/third_party-blink-renderer-core-frame-navigator.cc.patch new file mode 100644 index 000000000000..885386a1cd28 --- /dev/null +++ b/patches/third_party-blink-renderer-core-frame-navigator.cc.patch @@ -0,0 +1,12 @@ +diff --git a/third_party/blink/renderer/core/frame/navigator.cc b/third_party/blink/renderer/core/frame/navigator.cc +index b9eb3a925a366c6b556237b9c74464979e6c5fec..3a40559475e2aa51ff233e625108674f211b5d50 100644 +--- a/third_party/blink/renderer/core/frame/navigator.cc ++++ b/third_party/blink/renderer/core/frame/navigator.cc +@@ -76,6 +76,7 @@ String Navigator::userAgent() const { + if (!GetFrame() || !GetFrame()->GetPage()) + return String(); + ++ BRAVE_NAVIGATOR_USERAGENT + return GetFrame()->Loader().UserAgent(); + } + diff --git a/test/BUILD.gn b/test/BUILD.gn index 814bf8e09f3e..03a9478b464c 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -529,6 +529,7 @@ test("brave_browser_tests") { "//brave/browser/farbling/brave_enumeratedevices_farbling_browsertest.cc", "//brave/browser/farbling/brave_navigator_hardwareconcurrency_farbling_browsertest.cc", "//brave/browser/farbling/brave_navigator_plugins_farbling_browsertest.cc", + "//brave/browser/farbling/brave_navigator_useragent_farbling_browsertest.cc", "//brave/browser/farbling/brave_offscreencanvas_farbling_browsertest.cc", "//brave/browser/farbling/brave_webaudio_farbling_browsertest.cc", "//brave/browser/farbling/brave_webgl_farbling_browsertest.cc",