Skip to content

Commit

Permalink
Adapt Brave's implementation of navigator.plugins farbling
Browse files Browse the repository at this point in the history
Due to the removal of flash, navigator.plugins will always report an
empty array from now on, so Brave needs to adapt its farbling mechanism
to work like this depending on the selected behavior:

  * "off": return the real navigators.plugins, that is, an empty array.
  * "balanced" or "maximum": return 2 randomly generated fake plugins.

Additionally, we need to adapt the browser tests to account for this
new scenario, and test that indeed the expectation of navigator.plugins
returning an empty array stays true.

Issues in Brave's GitHub related to the implementation of this feature:

  * Fingerprinting 2.0: Plugins
    brave/brave-browser#9435

  * Fingerprinting 2.0: Plugins (word replacement)
    brave/brave-browser#10597

Chromium change:
https://chromium.googlesource.com/chromium/src.git/+/f8fe422e0e8a026f73f9d74d26f75c3c93102030

commit f8fe422e0e8a026f73f9d74d26f75c3c93102030
Author: Mason Freed <masonfreed@chromium.org>
Date:   Wed Jan 20 18:50:56 2021 +0000

    Empty out navigator.plugins and navigator.mimeTypes

    With this CL, navigator.plugins and navigator.mimeTypes will
    both return empty arrays.

    With the removal of Flash, there is no longer the need to
    return anything for navigator.plugins and navigator.mimeTypes.
    These APIs were used primarily for a) probing for Flash player
    support, or b) fingerprinting. As such, we'd like to return
    empty for these two properties. Gecko already switched to
    returning *only* Flash starting with Firefox 52, and returning
    empty as of Firefox 85.

    I2S:
    https://groups.google.com/a/chromium.org/g/blink-dev/c/bbxAGu90LgM

    Fixed: 1164635
  • Loading branch information
mariospr authored and mkarolin committed Mar 23, 2021
1 parent 232079b commit 61fb284
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 135 deletions.
66 changes: 26 additions & 40 deletions browser/farbling/brave_navigator_plugins_farbling_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -116,20 +116,23 @@ class BraveNavigatorPluginsFarblingBrowserTest : public InProcessBrowserTest {
IN_PROC_BROWSER_TEST_F(BraveNavigatorPluginsFarblingBrowserTest,
FarbleNavigatorPlugins) {
// Farbling level: off
// get real length of navigator.plugins
// get real length of navigator.plugins, which should return an empty array
// from Chromium 90 on (see https://crrev.com/c/2629990 for reference).
AllowFingerprinting();
NavigateToURLUntilLoadStop(farbling_url());
int off_length = ExecScriptGetInt(kPluginsLengthScript, contents());
int real_plugins_length = ExecScriptGetInt(kPluginsLengthScript, contents());
EXPECT_EQ(real_plugins_length, 0);

// Farbling level: balanced (default)
// navigator.plugins should contain all real plugins + 2 fake ones
// Farbling level: balanced (default) navigator.plugins should contain 2 fake
// ones (we used to report 2 real plugins and 2 fake ones before Chromium 90,
// but there are no real ones now, see https://crrev.com/c/2629990).
SetFingerprintingDefault();
NavigateToURLUntilLoadStop(farbling_url());
int balanced_length = ExecScriptGetInt(kPluginsLengthScript, contents());
EXPECT_EQ(balanced_length, off_length + 2);
EXPECT_EQ(balanced_length, 2);

// Farbling level: maximum
// navigator.plugins should contain no real plugins, only 2 fake ones
// navigator.plugins should contain no real plugins either, only 2 fake ones.
BlockFingerprinting();
NavigateToURLUntilLoadStop(farbling_url());
int maximum_length = ExecScriptGetInt(kPluginsLengthScript, contents());
Expand Down Expand Up @@ -188,65 +191,48 @@ IN_PROC_BROWSER_TEST_F(BraveNavigatorPluginsFarblingBrowserTest,
"qVKly58ePHDBgQoUqVKFix48.fvXLlSJ");
}

// Tests that names of built-in plugins get farbled by default
// https://github.com/brave/brave-browser/issues/10597
// Tests that no built-in plugins returned even when fingerprinting is allowed,
// and that only fake ones are exposed when "balanced" behavior is selected.
IN_PROC_BROWSER_TEST_F(BraveNavigatorPluginsFarblingBrowserTest,
FarbleNavigatorPluginsBuiltin) {
FarbleNavigatorPluginsNoBuiltinAvailable) {
// Farbling level: off
AllowFingerprinting();
NavigateToURLUntilLoadStop(farbling_url());
int off_length = ExecScriptGetInt(kPluginsLengthScript, contents());
EXPECT_EQ(off_length, 2);
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[0].name);",
contents()),
"Chrome PDF Plugin");
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[1].name);",
contents()),
"Chrome PDF Viewer");
int real_plugins_length = ExecScriptGetInt(kPluginsLengthScript, contents());
EXPECT_EQ(real_plugins_length, 0);

// Farbling level: balanced (default)
SetFingerprintingDefault();
NavigateToURLUntilLoadStop(farbling_url());
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[1].name);",
"domAutomationController.send(navigator.plugins[0].name);",
contents()),
"Brave PDF plug in");
"Xr1at27");
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[2].name);",
"domAutomationController.send(navigator.plugins[1].name);",
contents()),
"Chrome PDF and PS plug-in");
"8.fPHDhw");
}

// Tests that names of built-in plugins that get farbled will reset to their
// original names when fingerprinting is turned off
// https://github.com/brave/brave-browser/issues/11278
// Tests that the list of plugins returned by navigator.plugins will reset to
// being and empty one when fingerprinting is turned off.
IN_PROC_BROWSER_TEST_F(BraveNavigatorPluginsFarblingBrowserTest,
FarbleNavigatorPluginsReset) {
// Farbling level: balanced (default)
SetFingerprintingDefault();
NavigateToURLUntilLoadStop(farbling_url());
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[1].name);",
"domAutomationController.send(navigator.plugins[0].name);",
contents()),
"Brave PDF plug in");
"Xr1at27");
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[2].name);",
"domAutomationController.send(navigator.plugins[1].name);",
contents()),
"Chrome PDF and PS plug-in");
"8.fPHDhw");

// Farbling level: off
AllowFingerprinting();
NavigateToURLUntilLoadStop(farbling_url());
int off_length = ExecScriptGetInt(kPluginsLengthScript, contents());
EXPECT_EQ(off_length, 2);
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[0].name);",
contents()),
"Chrome PDF Plugin");
EXPECT_EQ(ExecScriptGetStr(
"domAutomationController.send(navigator.plugins[1].name);",
contents()),
"Chrome PDF Viewer");
int real_plugins_length = ExecScriptGetInt(kPluginsLengthScript, contents());
EXPECT_EQ(real_plugins_length, 0);
}
17 changes: 0 additions & 17 deletions chromium_src/third_party/blink/renderer/core/page/plugin_data.h

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
#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/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"

using blink::DOMPlugin;
using blink::DOMPluginArray;
Expand All @@ -26,30 +24,9 @@ using blink::Member;
using blink::MimeClassInfo;
using blink::PluginInfo;
using WTF::String;
using WTF::StringBuilder;

namespace brave {

String PluginReplacementName(std::mt19937_64* prng) {
std::vector<String> chrome{"Chrome ", "Chromium ", "Brave ",
"Web ", "Browser ", "OpenSource ",
"Online ", "JavaScript ", ""};
std::vector<String> pdf{"PDF ",
"Portable Document Format ",
"portable-document-format ",
"document ",
"doc ",
"PDF and PS ",
"com.adobe.pdf "};
std::vector<String> viewer{"Viewer", "Renderer", "Display", "Plugin",
"plug-in", "plug in", "extension", ""};
StringBuilder result;
result.Append(chrome[(*prng)() % chrome.size()]);
result.Append(pdf[(*prng)() % pdf.size()]);
result.Append(viewer[(*prng)() % viewer.size()]);
return result.ToString();
}

void FarblePlugins(DOMPluginArray* owner,
HeapVector<Member<DOMPlugin>>* dom_plugins) {
if (!owner->DomWindow())
Expand All @@ -61,36 +38,15 @@ void FarblePlugins(DOMPluginArray* owner,
case BraveFarblingLevel::OFF: {
break;
}
case BraveFarblingLevel::BALANCED:
case BraveFarblingLevel::MAXIMUM: {
dom_plugins->clear();
// "Maximum" behavior is clear existing plugins + "balanced" behavior,
// so fall through here.
U_FALLTHROUGH;
}
case BraveFarblingLevel::BALANCED: {
std::mt19937_64 prng = BraveSessionCache::From(*(frame->DomWindow()))
.MakePseudoRandomGenerator();
// 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++) {
auto plugin = frame->GetPluginData()->Plugins()[index];
String name = plugin->Name();
// Built-in plugins get their names and descriptions farbled as well.
if ((name == "Chrome PDF Plugin") || (name == "Chrome PDF Viewer")) {
plugin->SetName(PluginReplacementName(&prng));
plugin->SetFilename(
BraveSessionCache::From(*(frame->DomWindow()))
.GenerateRandomString(plugin->Filename().Ascii(), 32));
}
(*dom_plugins)[index] =
MakeGarbageCollected<DOMPlugin>(frame->DomWindow(), *plugin);
}
// Both "balanced" and "maximum" behaviors will return a list with 2 fake
// plugins only since Chromium 90, which changed the navigator.plugins
// array to always be an empty one unless features::kNavigatorPluginsEmpty
// is disabled with --disable-features (see https://crrev.com/c/2629990),
// which is a scenario not supported by Brave.
DCHECK(dom_plugins->IsEmpty());

// Add fake plugin #1.
auto* fake_plugin_info_1 = MakeGarbageCollected<PluginInfo>(
BraveSessionCache::From(*(frame->DomWindow()))
Expand All @@ -109,6 +65,7 @@ void FarblePlugins(DOMPluginArray* owner,
auto* fake_dom_plugin_1 = MakeGarbageCollected<DOMPlugin>(
frame->DomWindow(), *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->DomWindow()))
Expand All @@ -127,7 +84,10 @@ void FarblePlugins(DOMPluginArray* owner,
auto* fake_dom_plugin_2 = MakeGarbageCollected<DOMPlugin>(
frame->DomWindow(), *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->DomWindow()))
.MakePseudoRandomGenerator();
std::shuffle(dom_plugins->begin(), dom_plugins->end(), prng);
break;
}
Expand All @@ -139,7 +99,7 @@ void FarblePlugins(DOMPluginArray* owner,
} // namespace brave

#define BRAVE_DOM_PLUGINS_UPDATE_PLUGIN_DATA \
data->ResetPluginData(); \
GetPluginData()->ResetPluginData(); \
brave::FarblePlugins(this, &dom_plugins_);

#include "../../../../../../../third_party/blink/renderer/modules/plugins/dom_plugin_array.cc"
Expand Down
17 changes: 0 additions & 17 deletions patches/third_party-blink-renderer-core-page-plugin_data.h.patch

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +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 d75bf82ee420aa67fed211284da20934a6ace39a..c81fac42874465155847109cf629d4e28824ef0a 100644
index d75bf82ee420aa67fed211284da20934a6ace39a..f32822e36cbdd3ffea1d847df2c9effbfb394928 100644
--- a/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc
+++ b/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc
@@ -154,6 +154,7 @@ void DOMPluginArray::UpdatePluginData() {
}
}
@@ -131,6 +131,7 @@ PluginData* DOMPluginArray::GetPluginData() const {
void DOMPluginArray::UpdatePluginData() {
if (base::FeatureList::IsEnabled(features::kNavigatorPluginsEmpty)) {
dom_plugins_.clear();
+ BRAVE_DOM_PLUGINS_UPDATE_PLUGIN_DATA
return;
}
+ BRAVE_DOM_PLUGINS_UPDATE_PLUGIN_DATA
}

void DOMPluginArray::ContextDestroyed() {
PluginData* data = GetPluginData();

0 comments on commit 61fb284

Please sign in to comment.