Skip to content

Commit

Permalink
Implement navigator.userAgent farbling
Browse files Browse the repository at this point in the history
.

.

.

.

.

.

.

.

.

.

.

.

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
  • Loading branch information
pilgrim-brave committed Sep 8, 2020
1 parent 39b61ba commit 76bd866
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 76bd866

Please sign in to comment.