Bug 1333651 - Part 2: Spoofing Navigator object when 'privacy.resistFingerprinting' is true. r=arthuredelstein,Ehsan
authorTim Huang <tihuang@mozilla.com>
Thu, 01 Jun 2017 14:54:59 +0800
changeset 364970 76040a8fabaaa1484900aa7fa6937e275e5c0bfc
parent 364969 a9c101b46ab6138f390fb4128bb3da403bfe529c
child 364971 5131e38858f91107d1d198ffd8a4187be1ed1a87
push id32058
push userkwierso@gmail.com
push dateWed, 21 Jun 2017 01:24:44 +0000
treeherdermozilla-central@c55e582aee5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarthuredelstein, Ehsan
bugs1333651
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1333651 - Part 2: Spoofing Navigator object when 'privacy.resistFingerprinting' is true. r=arthuredelstein,Ehsan This patch makes navigator object to return spoofed value for fields have fingerprintable concerns. This changes the worker navigator as well. MozReview-Commit-ID: E2SLNZRfuVP
dom/base/Navigator.cpp
dom/workers/WorkerNavigator.cpp
netwerk/protocol/http/nsHttpHandler.cpp
toolkit/components/resistfingerprinting/nsRFPService.h
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -56,16 +56,17 @@
 #include "mozilla/StaticPtr.h"
 #include "Connection.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
 #include "nsGlobalWindow.h"
 #include "nsIIdleObserver.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
+#include "nsRFPService.h"
 #include "nsStringStream.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIStringStream.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "TimeManager.h"
 #include "nsStreamUtils.h"
 #include "WidgetUtils.h"
@@ -460,16 +461,23 @@ Navigator::GetPlatform(nsAString& aPlatf
   }
 }
 
 void
 Navigator::GetOscpu(nsAString& aOSCPU, CallerType aCallerType,
                     ErrorResult& aRv) const
 {
   if (aCallerType != CallerType::System) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aOSCPU.AssignLiteral(SPOOFED_OSCPU);
+      return;
+    }
+
     const nsAdoptingString& override =
       Preferences::GetString("general.oscpu.override");
 
     if (override) {
       aOSCPU = override;
       return;
     }
   }
@@ -512,17 +520,17 @@ Navigator::GetProduct(nsAString& aProduc
   aProduct.AssignLiteral("Gecko");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Navigator::GetProductSub(nsAString& aProductSub)
 {
   // Legacy build ID hardcoded for backward compatibility (bug 776376)
-  aProductSub.AssignLiteral("20100101");
+  aProductSub.AssignLiteral(LEGACY_BUILD_ID);
   return NS_OK;
 }
 
 nsMimeTypeArray*
 Navigator::GetMimeTypes(ErrorResult& aRv)
 {
   if (!mMimeTypes) {
     if (!mWindow) {
@@ -634,16 +642,22 @@ Navigator::OnLine()
   return !NS_IsOffline();
 }
 
 void
 Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
                       ErrorResult& aRv) const
 {
   if (aCallerType != CallerType::System) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aBuildID.AssignLiteral(LEGACY_BUILD_ID);
+      return;
+    }
     const nsAdoptingString& override =
       Preferences::GetString("general.buildID.override");
 
     if (override) {
       aBuildID = override;
       return;
     }
   }
@@ -1764,16 +1778,22 @@ Navigator::GetWindowFromGlobal(JSObject*
 }
 
 nsresult
 Navigator::GetPlatform(nsAString& aPlatform, bool aUsePrefOverriddenValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aUsePrefOverriddenValue) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aPlatform.AssignLiteral(SPOOFED_PLATFORM);
+      return NS_OK;
+    }
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.platform.override");
 
     if (override) {
       aPlatform = override;
       return NS_OK;
     }
   }
@@ -1809,16 +1829,22 @@ Navigator::GetPlatform(nsAString& aPlatf
 }
 
 /* static */ nsresult
 Navigator::GetAppVersion(nsAString& aAppVersion, bool aUsePrefOverriddenValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aUsePrefOverriddenValue) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aAppVersion.AssignLiteral(SPOOFED_APPVERSION);
+      return NS_OK;
+    }
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.appversion.override");
 
     if (override) {
       aAppVersion = override;
       return NS_OK;
     }
   }
@@ -1846,16 +1872,23 @@ Navigator::GetAppVersion(nsAString& aApp
 }
 
 /* static */ void
 Navigator::AppName(nsAString& aAppName, bool aUsePrefOverriddenValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aUsePrefOverriddenValue) {
+    // If fingerprinting resistance is on, we will spoof this value. See nsRFPService.h
+    // for details about spoofed values.
+    if (nsContentUtils::ShouldResistFingerprinting()) {
+      aAppName.AssignLiteral(SPOOFED_APPNAME);
+      return;
+    }
+
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.appname.override");
 
     if (override) {
       aAppName = override;
       return;
     }
   }
@@ -1871,17 +1904,20 @@ Navigator::ClearUserAgentCache()
 
 nsresult
 Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow,
                         bool aIsCallerChrome,
                         nsAString& aUserAgent)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!aIsCallerChrome) {
+  // We will skip the override and pass to httpHandler to get spoofed userAgent
+  // when 'privacy.resistFingerprinting' is true.
+  if (!aIsCallerChrome &&
+      !nsContentUtils::ShouldResistFingerprinting()) {
     const nsAdoptingString& override =
       mozilla::Preferences::GetString("general.useragent.override");
 
     if (override) {
       aUserAgent = override;
       return NS_OK;
     }
   }
@@ -1896,17 +1932,21 @@ Navigator::GetUserAgent(nsPIDOMWindowInn
   nsAutoCString ua;
   rv = service->GetUserAgent(ua);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   CopyASCIItoUTF16(ua, aUserAgent);
 
-  if (!aWindow) {
+  // When the caller is content, we will always return spoofed userAgent and
+  // ignore the User-Agent header from the document channel when
+  // 'privacy.resistFingerprinting' is true.
+  if (!aWindow ||
+      (nsContentUtils::ShouldResistFingerprinting() && !aIsCallerChrome)) {
     return NS_OK;
   }
 
   // Copy the User-Agent header from the document channel which has already been
   // subject to UA overrides.
   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
   if (!doc) {
     return NS_OK;
--- a/dom/workers/WorkerNavigator.cpp
+++ b/dom/workers/WorkerNavigator.cpp
@@ -8,16 +8,17 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/StorageManager.h"
 #include "mozilla/dom/WorkerNavigator.h"
 #include "mozilla/dom/WorkerNavigatorBinding.h"
 #include "mozilla/dom/network/Connection.h"
 
 #include "nsProxyRelease.h"
+#include "nsRFPService.h"
 #include "RuntimeService.h"
 
 #include "nsIDocument.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
@@ -74,49 +75,61 @@ WorkerNavigator::SetLanguages(const nsTA
 }
 
 void
 WorkerNavigator::GetAppName(nsString& aAppName, CallerType aCallerType) const
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
-  if (!mProperties.mAppNameOverridden.IsEmpty() &&
+  if ((!mProperties.mAppNameOverridden.IsEmpty() ||
+       workerPrivate->ResistFingerprintingEnabled()) &&
       !workerPrivate->UsesSystemPrincipal()) {
-    aAppName = mProperties.mAppNameOverridden;
+    // We will spoof this value when 'privacy.resistFingerprinting' is true.
+    // See nsRFPService.h for spoofed value.
+    aAppName = workerPrivate->ResistFingerprintingEnabled() ?
+      NS_LITERAL_STRING(SPOOFED_APPNAME) : mProperties.mAppNameOverridden;
   } else {
     aAppName = mProperties.mAppName;
   }
 }
 
 void
 WorkerNavigator::GetAppVersion(nsString& aAppVersion, CallerType aCallerType,
                                ErrorResult& aRv) const
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
-  if (!mProperties.mAppVersionOverridden.IsEmpty() &&
+  if ((!mProperties.mAppVersionOverridden.IsEmpty() ||
+       workerPrivate->ResistFingerprintingEnabled()) &&
       !workerPrivate->UsesSystemPrincipal()) {
-    aAppVersion = mProperties.mAppVersionOverridden;
+    // We will spoof this value when 'privacy.resistFingerprinting' is true.
+    // See nsRFPService.h for spoofed value.
+    aAppVersion = workerPrivate->ResistFingerprintingEnabled() ?
+      NS_LITERAL_STRING(SPOOFED_APPVERSION) : mProperties.mAppVersionOverridden;
   } else {
     aAppVersion = mProperties.mAppVersion;
   }
 }
 
 void
 WorkerNavigator::GetPlatform(nsString& aPlatform, CallerType aCallerType,
                              ErrorResult& aRv) const
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
 
-  if (!mProperties.mPlatformOverridden.IsEmpty() &&
+  if ((!mProperties.mPlatformOverridden.IsEmpty() ||
+       workerPrivate->ResistFingerprintingEnabled()) &&
       !workerPrivate->UsesSystemPrincipal()) {
-    aPlatform = mProperties.mPlatformOverridden;
+    // We will spoof this value when 'privacy.resistFingerprinting' is true.
+    // See nsRFPService.h for spoofed value.
+    aPlatform = workerPrivate->ResistFingerprintingEnabled() ?
+      NS_LITERAL_STRING(SPOOFED_PLATFORM) : mProperties.mPlatformOverridden;
   } else {
     aPlatform = mProperties.mPlatform;
   }
 }
 
 namespace {
 
 class GetUserAgentRunnable final : public WorkerMainThreadRunnable
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -51,16 +51,17 @@
 #include "nsHttpChannelAuthProvider.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsSocketTransportService2.h"
 #include "nsIOService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIXULRuntime.h"
 #include "nsCharSeparatedTokenizer.h"
+#include "nsRFPService.h"
 
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
 #include "mozilla/BasePrincipal.h"
 
@@ -438,18 +439,18 @@ nsHttpHandler::Init()
 
     // Generating the spoofed userAgent for fingerprinting resistance. We will
     // round the version to the nearest 10. By doing so, the anonymity group will
     // cover more versions instead of one version.
     uint32_t spoofedVersion = mAppVersion.ToInteger(&rv);
     if (NS_SUCCEEDED(rv)) {
         spoofedVersion = spoofedVersion - (spoofedVersion % 10);
         mSpoofedUserAgent.Assign(nsPrintfCString(
-            "Mozilla/5.0 (Windows NT 6.1; rv:%d.0) Gecko/20100101 Firefox/%d.0",
-            spoofedVersion, spoofedVersion));
+            "Mozilla/5.0 (%s; rv:%d.0) Gecko/%s Firefox/%d.0",
+            SPOOFED_OSCPU, spoofedVersion, LEGACY_BUILD_ID, spoofedVersion));
     }
 
     mSessionStartTime = NowInSeconds();
     mHandlerActive = true;
 
     rv = mAuthCache.Init();
     if (NS_FAILED(rv)) return rv;
 
@@ -460,17 +461,17 @@ nsHttpHandler::Init()
     if (NS_FAILED(rv)) return rv;
 
     mRequestContextService =
         do_GetService("@mozilla.org/network/request-context-service;1");
 
 #if defined(ANDROID) || defined(MOZ_MULET)
     mProductSub.AssignLiteral(MOZILLA_UAVERSION);
 #else
-    mProductSub.AssignLiteral("20100101");
+    mProductSub.AssignLiteral(LEGACY_BUILD_ID);
 #endif
 
 #if DEBUG
     // dump user agent prefs
     LOG(("> legacy-app-name = %s\n", mLegacyAppName.get()));
     LOG(("> legacy-app-version = %s\n", mLegacyAppVersion.get()));
     LOG(("> platform = %s\n", mPlatform.get()));
     LOG(("> oscpu = %s\n", mOscpu.get()));
--- a/toolkit/components/resistfingerprinting/nsRFPService.h
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -6,16 +6,25 @@
 #ifndef __nsRFPService_h__
 #define __nsRFPService_h__
 
 #include "mozilla/Atomics.h"
 #include "nsIObserver.h"
 
 #include "nsString.h"
 
+// Defines regarding spoofed values of Navigator object. These spoofed values
+// are returned when 'privacy.resistFingerprinting' is true.
+#define SPOOFED_APPNAME    "Netscape"
+#define SPOOFED_APPVERSION "5.0 (Windows)"
+#define SPOOFED_OSCPU      "Windows NT 6.1"
+#define SPOOFED_PLATFORM   "Win32"
+
+#define LEGACY_BUILD_ID    "20100101"
+
 namespace mozilla {
 
 class nsRFPService final : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER