Skip to content

Commit

Permalink
Implement navigator.plugins farbling
Browse files Browse the repository at this point in the history
feedback and known-value tests

fix WebGL farbling test
  • Loading branch information
pilgrim-brave committed Jul 7, 2020
1 parent ad3b5bd commit 2794b2b
Show file tree
Hide file tree
Showing 7 changed files with 291 additions and 3 deletions.
171 changes: 171 additions & 0 deletions browser/farbling/brave_navigator_plugins_farbling_browsertest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/* 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 kPluginsLengthScript[] =
"domAutomationController.send(navigator.plugins.length);";

class BraveNavigatorPluginsFarblingBrowserTest : 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());

top_level_page_url_ = embedded_test_server()->GetURL("a.com", "/");
farbling_url_ = embedded_test_server()->GetURL("a.com", "/simple.html");
}

void TearDown() override {
browser_content_client_.reset();
content_client_.reset();
}

const GURL& farbling_url() { return farbling_url_; }

HostContentSettingsMap* content_settings() {
return HostContentSettingsMapFactory::GetForProfile(browser()->profile());
}

void AllowFingerprinting() {
brave_shields::SetFingerprintingControlType(
content_settings(), ControlType::ALLOW, top_level_page_url_);
}

void BlockFingerprinting() {
brave_shields::SetFingerprintingControlType(
content_settings(), ControlType::BLOCK, top_level_page_url_);
}

void SetFingerprintingDefault() {
brave_shields::SetFingerprintingControlType(
content_settings(), ControlType::DEFAULT, top_level_page_url_);
}

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:
GURL top_level_page_url_;
GURL farbling_url_;
std::unique_ptr<ChromeContentClient> content_client_;
std::unique_ptr<BraveContentBrowserClient> browser_content_client_;
};

// Tests results of farbling known values
IN_PROC_BROWSER_TEST_F(BraveNavigatorPluginsFarblingBrowserTest,
FarbleNavigatorPlugins) {
// Farbling level: off
// get real length of navigator.plugins
AllowFingerprinting();
NavigateToURLUntilLoadStop(farbling_url());
int off_length = ExecScriptGetInt(kPluginsLengthScript, contents());

// Farbling level: balanced (default)
// navigator.plugins should contain all real plugins + 2 fake ones
SetFingerprintingDefault();
NavigateToURLUntilLoadStop(farbling_url());
int balanced_length = ExecScriptGetInt(kPluginsLengthScript, contents());
EXPECT_EQ(balanced_length, off_length + 2);

// Farbling level: maximum
// navigator.plugins should contain no real plugins, only 2 fake ones
BlockFingerprinting();
NavigateToURLUntilLoadStop(farbling_url());
int maximum_length = ExecScriptGetInt(kPluginsLengthScript, contents());
EXPECT_EQ(maximum_length, 2);
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[0].name);",
contents()),
"Xr1at27");
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[0].filename);",
contents()),
"SJEChw48ev3bNGD");
EXPECT_EQ(
ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[0].description);",
contents()),
"rVqVqVqVqVKlSpUqVqVKlSJEChQIECh");
EXPECT_EQ(ExecScriptGetInt(
"domAutomationController.send(navigator.plugins[0].length);",
contents()),
0);
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[1].name);",
contents()),
"8.fPHDhw");
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[1].filename);",
contents()),
"06du37du3bt2bNmT");
EXPECT_EQ(
ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[1].description);",
contents()),
"BgwYMmTpUq1aNmTJky5cOnTp069ePnTp");
EXPECT_EQ(ExecScriptGetInt(
"domAutomationController.send(navigator.plugins[1].length);",
contents()),
0);
}
2 changes: 1 addition & 1 deletion browser/farbling/brave_webgl_farbling_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ class BraveWebGLFarblingBrowserTest : public InProcessBrowserTest {
};

IN_PROC_BROWSER_TEST_F(BraveWebGLFarblingBrowserTest, FarbleGetParameter) {
const std::string kExpectedRandomString = "kSJEiRIk,ix4cuXLF";
const std::string kExpectedRandomString = "UKlSpUqV,TJEix48e";
// Farbling level: maximum
// WebGL getParameter of restricted values: pseudo-random data with no
// relation to original data
Expand Down
9 changes: 7 additions & 2 deletions chromium_src/third_party/blink/renderer/core/dom/document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ WTF::String BraveSessionCache::GenerateRandomString(std::string seed,
wtf_size_t length) {
uint8_t key[32];
crypto::HMAC h(crypto::HMAC::SHA256);
CHECK(h.Init(reinterpret_cast<const unsigned char*>(&session_key_),
sizeof session_key_));
CHECK(h.Init(reinterpret_cast<const unsigned char*>(&domain_key_),
sizeof domain_key_));
CHECK(h.Sign(seed, key, sizeof key));
// initial PRNG seed based on session key and passed-in seed string
uint64_t v = *reinterpret_cast<uint64_t*>(key);
Expand All @@ -241,6 +241,11 @@ WTF::String BraveSessionCache::GenerateRandomString(std::string seed,
return value;
}

std::mt19937_64 BraveSessionCache::MakePseudoRandomGenerator() {
uint64_t seed = *reinterpret_cast<uint64_t*>(domain_key_);
return std::mt19937_64(seed);
}

} // namespace brave

#include "../../../../../../../third_party/blink/renderer/core/dom/document.cc"
3 changes: 3 additions & 0 deletions chromium_src/third_party/blink/renderer/core/dom/document.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

#include "../../../../../../../third_party/blink/renderer/core/dom/document.h"

#include <random>

#include "base/callback.h"

using blink::Document;
Expand Down Expand Up @@ -46,6 +48,7 @@ class CORE_EXPORT BraveSessionCache final
blink::LocalFrame* frame,
scoped_refptr<blink::StaticBitmapImage> image_bitmap);
WTF::String GenerateRandomString(std::string seed, wtf_size_t length);
std::mt19937_64 MakePseudoRandomGenerator();

private:
bool farbling_enabled_;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* 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 <random>

#include "brave/third_party/blink/renderer/brave_farbling_constants.h"
#include "third_party/blink/public/platform/web_content_settings_client.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/page/plugin_data.h"
#include "third_party/blink/renderer/modules/plugins/dom_plugin.h"
#include "third_party/blink/renderer/modules/plugins/dom_plugin_array.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"

using blink::DOMPlugin;
using blink::DOMPluginArray;
using blink::HeapVector;
using blink::LocalFrame;
using blink::MakeGarbageCollected;
using blink::Member;
using blink::PluginInfo;

namespace brave {

void FarblePlugins(DOMPluginArray* owner,
HeapVector<Member<DOMPlugin>>* dom_plugins) {
LocalFrame* frame = owner->GetFrame();
if (!frame || !frame->GetContentSettingsClient())
return;
switch (frame->GetContentSettingsClient()->GetBraveFarblingLevel()) {
case BraveFarblingLevel::OFF: {
break;
}
case BraveFarblingLevel::MAXIMUM: {
dom_plugins->clear();
// "Maximum" behavior is clear existing plugins + "balanced" behavior,
// so fall through here.
U_FALLTHROUGH;
}
case BraveFarblingLevel::BALANCED: {
// The item() method will populate plugin info if any item of
// |dom_plugins_| is null, but when it tries, it assumes the
// length of |dom_plugins_| == the length of the underlying
// GetPluginData()->Plugins(). Once we add our fake plugins, that
// assumption will break and the item() method will crash with an
// out-of-bounds array access. Rather than patch the item() method, we
// ensure that the cache is fully populated now while the assumptions
// still hold, so the problematic code is never executed later.
for (unsigned index = 0; index < dom_plugins->size(); index++) {
(*dom_plugins)[index] = owner->item(index);
}
// Add fake plugin #1.
auto* fake_plugin_info_1 = MakeGarbageCollected<PluginInfo>(
BraveSessionCache::From(*(frame->GetDocument()))
.GenerateRandomString("PLUGIN_1_NAME", 8),
BraveSessionCache::From(*(frame->GetDocument()))
.GenerateRandomString("PLUGIN_1_FILENAME", 16),
BraveSessionCache::From(*(frame->GetDocument()))
.GenerateRandomString("PLUGIN_1_DESCRIPTION", 32),
0, false);
auto* fake_dom_plugin_1 =
MakeGarbageCollected<DOMPlugin>(frame, *fake_plugin_info_1);
dom_plugins->push_back(fake_dom_plugin_1);
// Add fake plugin #2.
auto* fake_plugin_info_2 = MakeGarbageCollected<PluginInfo>(
BraveSessionCache::From(*(frame->GetDocument()))
.GenerateRandomString("PLUGIN_2_NAME", 7),
BraveSessionCache::From(*(frame->GetDocument()))
.GenerateRandomString("PLUGIN_2_FILENAME", 15),
BraveSessionCache::From(*(frame->GetDocument()))
.GenerateRandomString("PLUGIN_2_DESCRIPTION", 31),
0, false);
auto* fake_dom_plugin_2 =
MakeGarbageCollected<DOMPlugin>(frame, *fake_plugin_info_2);
dom_plugins->push_back(fake_dom_plugin_2);
// Shuffle the list of plugins pseudo-randomly, based on the domain key.
std::mt19937_64 prng = BraveSessionCache::From(*(frame->GetDocument()))
.MakePseudoRandomGenerator();
std::shuffle(dom_plugins->begin(), dom_plugins->end(), prng);
break;
}
default:
NOTREACHED();
}
}

} // namespace brave

#define BRAVE_DOM_PLUGINS_UPDATE_PLUGIN_DATA \
brave::FarblePlugins(this, &dom_plugins_);

#include "../../../../../../third_party/blink/renderer/modules/plugins/dom_plugin_array.cc"

#undef BRAVE_DOM_PLUGIN_ARRAY_GET_PLUGIN_DATA
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc b/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc
index 322e74880ddaa6c6c54f80f4f5f7ab0264631ffd..0c13152cb2f9ebd5bbf86955f50eab6909f05443 100644
--- a/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc
+++ b/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc
@@ -146,6 +146,7 @@ void DOMPluginArray::UpdatePluginData() {
}
}
}
+ BRAVE_DOM_PLUGINS_UPDATE_PLUGIN_DATA
}

void DOMPluginArray::ContextDestroyed() {
1 change: 1 addition & 0 deletions test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ test("brave_browser_tests") {
"//brave/browser/extensions/brave_extension_functional_test.h",
"//brave/browser/extensions/brave_extension_provider_browsertest.cc",
"//brave/browser/extensions/brave_theme_event_router_browsertest.cc",
"//brave/browser/farbling/brave_navigator_plugins_farbling_browsertest.cc",
"//brave/browser/farbling/brave_webaudio_farbling_browsertest.cc",
"//brave/browser/farbling/brave_webgl_farbling_browsertest.cc",
"//brave/browser/net/brave_network_delegate_browsertest.cc",
Expand Down

0 comments on commit 2794b2b

Please sign in to comment.