From 01c092f53494173fbea657d0b839abdeb567206c Mon Sep 17 00:00:00 2001 From: Mario Sanchez Prada Date: Fri, 29 Jan 2021 12:41:59 +0100 Subject: [PATCH] Adapt Brave's implementation of navigator.plugins farbling 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 https://github.com/brave/brave-browser/issues/9435 * Fingerprinting 2.0: Plugins (word replacement) https://github.com/brave/brave-browser/issues/10597 Chromium change: https://chromium.googlesource.com/chromium/src.git/+/f8fe422e0e8a026f73f9d74d26f75c3c93102030 commit f8fe422e0e8a026f73f9d74d26f75c3c93102030 Author: Mason Freed 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 --- ..._navigator_plugins_farbling_browsertest.cc | 66 ++++++++----------- .../blink/renderer/core/page/plugin_data.h | 17 ----- .../modules/plugins/dom_plugin_array.cc | 66 ++++--------------- ...ink-renderer-core-page-plugin_data.h.patch | 17 ----- ...-modules-plugins-dom_plugin_array.cc.patch | 16 ++--- 5 files changed, 47 insertions(+), 135 deletions(-) delete mode 100644 chromium_src/third_party/blink/renderer/core/page/plugin_data.h delete mode 100644 patches/third_party-blink-renderer-core-page-plugin_data.h.patch diff --git a/browser/farbling/brave_navigator_plugins_farbling_browsertest.cc b/browser/farbling/brave_navigator_plugins_farbling_browsertest.cc index a4e074476b84..9f080f148d2e 100644 --- a/browser/farbling/brave_navigator_plugins_farbling_browsertest.cc +++ b/browser/farbling/brave_navigator_plugins_farbling_browsertest.cc @@ -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()); @@ -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); } diff --git a/chromium_src/third_party/blink/renderer/core/page/plugin_data.h b/chromium_src/third_party/blink/renderer/core/page/plugin_data.h deleted file mode 100644 index 5fc591ef49eb..000000000000 --- a/chromium_src/third_party/blink/renderer/core/page/plugin_data.h +++ /dev/null @@ -1,17 +0,0 @@ -/* 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_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_PLUGIN_DATA_H_ -#define BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_PLUGIN_DATA_H_ - -#define BRAVE_PLUGIN_DATA_H \ - void SetName(const String& new_name) { name_ = new_name; } \ - void SetFilename(const String& new_filename) { filename_ = new_filename; } - -#include "../../../../../../../third_party/blink/renderer/core/page/plugin_data.h" - -#undef BRAVE_PLUGIN_DATA_H - -#endif // BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_PAGE_PLUGIN_DATA_H_ diff --git a/chromium_src/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc b/chromium_src/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc index 04088e99edb7..a5569681e0a3 100644 --- a/chromium_src/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc +++ b/chromium_src/third_party/blink/renderer/modules/plugins/dom_plugin_array.cc @@ -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; @@ -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 chrome{"Chrome ", "Chromium ", "Brave ", - "Web ", "Browser ", "OpenSource ", - "Online ", "JavaScript ", ""}; - std::vector pdf{"PDF ", - "Portable Document Format ", - "portable-document-format ", - "document ", - "doc ", - "PDF and PS ", - "com.adobe.pdf "}; - std::vector 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>* dom_plugins) { if (!owner->DomWindow()) @@ -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(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( BraveSessionCache::From(*(frame->DomWindow())) @@ -109,6 +65,7 @@ void FarblePlugins(DOMPluginArray* owner, auto* fake_dom_plugin_1 = MakeGarbageCollected( frame->DomWindow(), *fake_plugin_info_1); dom_plugins->push_back(fake_dom_plugin_1); + // Add fake plugin #2. auto* fake_plugin_info_2 = MakeGarbageCollected( BraveSessionCache::From(*(frame->DomWindow())) @@ -127,7 +84,10 @@ void FarblePlugins(DOMPluginArray* owner, auto* fake_dom_plugin_2 = MakeGarbageCollected( 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; } @@ -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" diff --git a/patches/third_party-blink-renderer-core-page-plugin_data.h.patch b/patches/third_party-blink-renderer-core-page-plugin_data.h.patch deleted file mode 100644 index e5e1d862a772..000000000000 --- a/patches/third_party-blink-renderer-core-page-plugin_data.h.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/third_party/blink/renderer/core/page/plugin_data.h b/third_party/blink/renderer/core/page/plugin_data.h -index fe5aa20243d89ea6a0f1cecb5ea00846607c6d39..7363a4dab1c30ab26fc57b2d546b77564b359e0b 100644 ---- a/third_party/blink/renderer/core/page/plugin_data.h -+++ b/third_party/blink/renderer/core/page/plugin_data.h -@@ -1,4 +1,3 @@ -- - /* - Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - -@@ -76,6 +75,7 @@ class CORE_EXPORT PluginInfo final : public GarbageCollected { - Color BackgroundColor() const { return background_color_; } - bool MayUseExternalHandler() const { return may_use_external_handler_; } - -+ BRAVE_PLUGIN_DATA_H - private: - friend class MimeClassInfo; - friend class PluginData; diff --git a/patches/third_party-blink-renderer-modules-plugins-dom_plugin_array.cc.patch b/patches/third_party-blink-renderer-modules-plugins-dom_plugin_array.cc.patch index c3b5496ecb48..8246b98f2199 100644 --- a/patches/third_party-blink-renderer-modules-plugins-dom_plugin_array.cc.patch +++ b/patches/third_party-blink-renderer-modules-plugins-dom_plugin_array.cc.patch @@ -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();