Skip to content

Commit

Permalink
Merge pull request #6055 from brave/mpilgrim_navigator_ua
Browse files Browse the repository at this point in the history
Implement navigator.userAgent farbling
  • Loading branch information
pilgrim-brave authored Sep 8, 2020
2 parents e4486bd + 76bd866 commit 544edd6
Show file tree
Hide file tree
Showing 10 changed files with 471 additions and 0 deletions.
1 change: 1 addition & 0 deletions browser/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ source_set("browser_process") {
"autoplay",
"content_settings",
"download",
"farbling",
"net",
"profiles",
"renderer_context_menu",
Expand Down
3 changes: 3 additions & 0 deletions browser/brave_tab_helpers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -123,6 +124,8 @@ void AttachTabHelpers(content::WebContents* web_contents) {
ipfs::IPFSTabHelper::CreateForWebContents(web_contents);
}
#endif

FarblingTabHelper::CreateForWebContents(web_contents);
}

} // namespace brave
18 changes: 18 additions & 0 deletions browser/farbling/BUILD.gn
Original file line number Diff line number Diff line change
@@ -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",
]
}
161 changes: 161 additions & 0 deletions browser/farbling/brave_navigator_useragent_farbling_browsertest.cc
Original file line number Diff line number Diff line change
@@ -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 <typename T>
int ExecScriptGetInt(const std::string& script, T* frame) {
int value;
EXPECT_TRUE(ExecuteScriptAndExtractInt(frame, script, &value));
return value;
}

template <typename T>
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<ChromeContentClient> content_client_;
std::unique_ptr<BraveContentBrowserClient> 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);
}
162 changes: 162 additions & 0 deletions browser/farbling/farbling_tab_helper.cc
Original file line number Diff line number Diff line change
@@ -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 <algorithm>
#include <string>

#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<Profile*>(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
Loading

0 comments on commit 544edd6

Please sign in to comment.