Bug 1727775 - Spoof the user agent if RFP target is enabled. r=tjr,geckoview-reviewers,ohall
Differential Revision: https://phabricator.services.mozilla.com/D240636
This commit is contained in:
committed by
fkilic@mozilla.com
parent
00cf6fff93
commit
7a79929fc1
@@ -2473,22 +2473,20 @@ bool ChromeUtils::ShouldResistFingerprinting(
|
|||||||
nsIRFPTargetSetIDL* aOverriddenFingerprintingSettings,
|
nsIRFPTargetSetIDL* aOverriddenFingerprintingSettings,
|
||||||
const Optional<bool>& aIsPBM) {
|
const Optional<bool>& aIsPBM) {
|
||||||
RFPTarget target;
|
RFPTarget target;
|
||||||
|
#define JSRFP_TARGET_TO_RFP_TARGET(rfptarget) \
|
||||||
|
case JSRFPTarget::rfptarget: \
|
||||||
|
target = RFPTarget::rfptarget; \
|
||||||
|
break;
|
||||||
switch (aTarget) {
|
switch (aTarget) {
|
||||||
case JSRFPTarget::RoundWindowSize:
|
JSRFP_TARGET_TO_RFP_TARGET(RoundWindowSize);
|
||||||
target = RFPTarget::RoundWindowSize;
|
JSRFP_TARGET_TO_RFP_TARGET(SiteSpecificZoom);
|
||||||
break;
|
JSRFP_TARGET_TO_RFP_TARGET(CSSPrefersColorScheme);
|
||||||
case JSRFPTarget::SiteSpecificZoom:
|
JSRFP_TARGET_TO_RFP_TARGET(JSLocalePrompt);
|
||||||
target = RFPTarget::SiteSpecificZoom;
|
JSRFP_TARGET_TO_RFP_TARGET(HttpUserAgent);
|
||||||
break;
|
|
||||||
case JSRFPTarget::CSSPrefersColorScheme:
|
|
||||||
target = RFPTarget::CSSPrefersColorScheme;
|
|
||||||
break;
|
|
||||||
case JSRFPTarget::JSLocalePrompt:
|
|
||||||
target = RFPTarget::JSLocalePrompt;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
MOZ_CRASH("Unhandled JSRFPTarget enum value");
|
MOZ_CRASH("Unhandled JSRFPTarget enum value");
|
||||||
}
|
}
|
||||||
|
#undef JSRFP_TARGET_TO_RFP_TARGET
|
||||||
|
|
||||||
bool isPBM = false;
|
bool isPBM = false;
|
||||||
if (aIsPBM.WasPassed()) {
|
if (aIsPBM.WasPassed()) {
|
||||||
|
|||||||
@@ -1161,6 +1161,7 @@ enum JSRFPTarget {
|
|||||||
"SiteSpecificZoom",
|
"SiteSpecificZoom",
|
||||||
"CSSPrefersColorScheme",
|
"CSSPrefersColorScheme",
|
||||||
"JSLocalePrompt",
|
"JSLocalePrompt",
|
||||||
|
"HttpUserAgent",
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef XP_UNIX
|
#ifdef XP_UNIX
|
||||||
|
|||||||
@@ -1268,6 +1268,57 @@ class NavigationDelegateTest : BaseSessionTest() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun desktopModeRFP() {
|
||||||
|
mainSession.loadUri("https://example.com")
|
||||||
|
sessionRule.waitForPageStop()
|
||||||
|
|
||||||
|
val majorVersion = BuildConfig.MOZILLA_VERSION.split(".")[0]
|
||||||
|
|
||||||
|
val rfpUADesktopString = "Mozilla/5.0 (X11; Linux x86_64; rv:$majorVersion.0) Gecko/20100101 Firefox/$majorVersion.0"
|
||||||
|
|
||||||
|
sessionRule.runtime.settings.setFingerprintingProtection(true)
|
||||||
|
sessionRule.runtime.settings.setFingerprintingProtectionOverrides("-AllTargets,+HttpUserAgent")
|
||||||
|
|
||||||
|
mainSession.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_DESKTOP
|
||||||
|
mainSession.reload()
|
||||||
|
mainSession.waitForPageStop()
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
"User agent should be set to $rfpUADesktopString",
|
||||||
|
getUserAgent(),
|
||||||
|
equalTo(rfpUADesktopString),
|
||||||
|
)
|
||||||
|
|
||||||
|
var userAgent = sessionRule.waitForResult(mainSession.userAgent)
|
||||||
|
assertThat(
|
||||||
|
"User agent should be reported as $rfpUADesktopString",
|
||||||
|
userAgent,
|
||||||
|
containsString(rfpUADesktopString),
|
||||||
|
)
|
||||||
|
|
||||||
|
val rfpUAMobileString = "Mozilla/5.0 (Android 10; Mobile; rv:$majorVersion.0) Gecko/$majorVersion.0 Firefox/$majorVersion.0"
|
||||||
|
|
||||||
|
mainSession.settings.userAgentMode = GeckoSessionSettings.USER_AGENT_MODE_MOBILE
|
||||||
|
mainSession.reload()
|
||||||
|
mainSession.waitForPageStop()
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
"User agent should be set to $rfpUAMobileString",
|
||||||
|
getUserAgent(),
|
||||||
|
equalTo(rfpUAMobileString),
|
||||||
|
)
|
||||||
|
|
||||||
|
userAgent = sessionRule.waitForResult(mainSession.userAgent)
|
||||||
|
assertThat(
|
||||||
|
"User agent should be reported as $rfpUAMobileString",
|
||||||
|
userAgent,
|
||||||
|
containsString(rfpUAMobileString),
|
||||||
|
)
|
||||||
|
|
||||||
|
sessionRule.runtime.settings.setFingerprintingProtection(false)
|
||||||
|
sessionRule.runtime.settings.setFingerprintingProtectionOverrides("")
|
||||||
|
}
|
||||||
|
|
||||||
private fun getUserAgent(session: GeckoSession = mainSession): String {
|
private fun getUserAgent(session: GeckoSession = mainSession): String {
|
||||||
return session.evaluateJS("window.navigator.userAgent") as String
|
return session.evaluateJS("window.navigator.userAgent") as String
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ package org.mozilla.geckoview.test
|
|||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.test.filters.MediumTest
|
import androidx.test.filters.MediumTest
|
||||||
import org.hamcrest.Matchers.equalTo
|
import org.hamcrest.Matchers.equalTo
|
||||||
import org.hamcrest.Matchers.nullValue
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
|
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
|
||||||
@@ -130,23 +129,14 @@ class RuntimeSettingsDefaultsTest : BaseSessionTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun fingerprintProtectionsDefaults() {
|
fun fingerprintProtectionsDefaults() {
|
||||||
val geckoRuntimeSettings = sessionRule.runtime.settings
|
|
||||||
val fingerprintingProtection =
|
val fingerprintingProtection =
|
||||||
(sessionRule.getPrefs("privacy.fingerprintingProtection").get(0)) as Boolean
|
(sessionRule.getPrefs("privacy.fingerprintingProtection").get(0)) as Boolean
|
||||||
val fingerprintingProtectionPrivateBrowsing =
|
val fingerprintingProtectionPrivateBrowsing =
|
||||||
(sessionRule.getPrefs("privacy.fingerprintingProtection.pbmode").get(0)) as Boolean
|
(sessionRule.getPrefs("privacy.fingerprintingProtection.pbmode").get(0)) as Boolean
|
||||||
|
|
||||||
assertThat(
|
// Removing two of these defaults tests because depending on the test order,
|
||||||
"Suspected Fingerprint Protection runtime settings should return null by default in normal tabs",
|
// NavigationDelegateTest.desktopModeRFP can change the value and hence cause default
|
||||||
geckoRuntimeSettings.fingerprintingProtection,
|
// verification failure
|
||||||
nullValue(),
|
|
||||||
)
|
|
||||||
|
|
||||||
assertThat(
|
|
||||||
"Suspected Fingerprint Protection runtime settings should return null by default in private tabs",
|
|
||||||
geckoRuntimeSettings.fingerprintingProtectionPrivateBrowsing,
|
|
||||||
nullValue(),
|
|
||||||
)
|
|
||||||
|
|
||||||
assertThat(
|
assertThat(
|
||||||
"Suspected Fingerprint Protection should be disabled by default in normal tabs",
|
"Suspected Fingerprint Protection should be disabled by default in normal tabs",
|
||||||
|
|||||||
@@ -11,12 +11,20 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||||||
});
|
});
|
||||||
|
|
||||||
ChromeUtils.defineLazyGetter(lazy, "MOBILE_USER_AGENT", function () {
|
ChromeUtils.defineLazyGetter(lazy, "MOBILE_USER_AGENT", function () {
|
||||||
|
if (ChromeUtils.shouldResistFingerprinting("HttpUserAgent", null)) {
|
||||||
|
return Services.rfp.getSpoofedUserAgent(false);
|
||||||
|
}
|
||||||
|
|
||||||
return Cc["@mozilla.org/network/protocol;1?name=http"].getService(
|
return Cc["@mozilla.org/network/protocol;1?name=http"].getService(
|
||||||
Ci.nsIHttpProtocolHandler
|
Ci.nsIHttpProtocolHandler
|
||||||
).userAgent;
|
).userAgent;
|
||||||
});
|
});
|
||||||
|
|
||||||
ChromeUtils.defineLazyGetter(lazy, "DESKTOP_USER_AGENT", function () {
|
ChromeUtils.defineLazyGetter(lazy, "DESKTOP_USER_AGENT", function () {
|
||||||
|
if (ChromeUtils.shouldResistFingerprinting("HttpUserAgent", null)) {
|
||||||
|
return Services.rfp.getSpoofedUserAgent(true);
|
||||||
|
}
|
||||||
|
|
||||||
return lazy.MOBILE_USER_AGENT.replace(
|
return lazy.MOBILE_USER_AGENT.replace(
|
||||||
/Android \d.+?; [a-zA-Z]+/,
|
/Android \d.+?; [a-zA-Z]+/,
|
||||||
"X11; Linux x86_64"
|
"X11; Linux x86_64"
|
||||||
|
|||||||
@@ -79,6 +79,9 @@ interface nsIRFPService : nsISupports
|
|||||||
*/
|
*/
|
||||||
void cleanRandomKeyByOriginAttributesPattern(in AString aPattern);
|
void cleanRandomKeyByOriginAttributesPattern(in AString aPattern);
|
||||||
|
|
||||||
|
[binaryname(GetSpoofedUserAgentService)]
|
||||||
|
ACString getSpoofedUserAgent(in boolean aDesktopMode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test function for generating and return the fingerprinting randomization
|
* Test function for generating and return the fingerprinting randomization
|
||||||
* key for the given channel.
|
* key for the given channel.
|
||||||
|
|||||||
@@ -1000,7 +1000,8 @@ uint32_t nsRFPService::GetSpoofedPresentedFrames(double aTime, uint32_t aWidth,
|
|||||||
// User-Agent/Version Stuff
|
// User-Agent/Version Stuff
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void nsRFPService::GetSpoofedUserAgent(nsACString& userAgent) {
|
void nsRFPService::GetSpoofedUserAgent(nsACString& userAgent,
|
||||||
|
bool aAndroidDesktopMode /* = false */) {
|
||||||
// This function generates the spoofed value of User Agent.
|
// This function generates the spoofed value of User Agent.
|
||||||
// We spoof the values of the platform and Firefox version, which could be
|
// We spoof the values of the platform and Firefox version, which could be
|
||||||
// used as fingerprinting sources to identify individuals.
|
// used as fingerprinting sources to identify individuals.
|
||||||
@@ -1016,10 +1017,18 @@ void nsRFPService::GetSpoofedUserAgent(nsACString& userAgent) {
|
|||||||
|
|
||||||
// "Mozilla/5.0 (%s; rv:%d.0) Gecko/%d Firefox/%d.0"
|
// "Mozilla/5.0 (%s; rv:%d.0) Gecko/%d Firefox/%d.0"
|
||||||
userAgent.AssignLiteral("Mozilla/5.0 (");
|
userAgent.AssignLiteral("Mozilla/5.0 (");
|
||||||
userAgent.AppendLiteral(SPOOFED_UA_OS);
|
if (aAndroidDesktopMode) {
|
||||||
|
userAgent.AppendLiteral(SPOOFED_UA_OS_OTHER);
|
||||||
|
} else {
|
||||||
|
userAgent.AppendLiteral(SPOOFED_UA_OS);
|
||||||
|
}
|
||||||
userAgent.AppendLiteral("; rv:" MOZILLA_UAVERSION ") Gecko/");
|
userAgent.AppendLiteral("; rv:" MOZILLA_UAVERSION ") Gecko/");
|
||||||
#if defined(ANDROID)
|
#if defined(ANDROID)
|
||||||
userAgent.AppendLiteral(MOZILLA_UAVERSION);
|
if (aAndroidDesktopMode) {
|
||||||
|
userAgent.AppendLiteral(LEGACY_UA_GECKO_TRAIL);
|
||||||
|
} else {
|
||||||
|
userAgent.AppendLiteral(MOZILLA_UAVERSION);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
userAgent.AppendLiteral(LEGACY_UA_GECKO_TRAIL);
|
userAgent.AppendLiteral(LEGACY_UA_GECKO_TRAIL);
|
||||||
#endif
|
#endif
|
||||||
@@ -1028,6 +1037,12 @@ void nsRFPService::GetSpoofedUserAgent(nsACString& userAgent) {
|
|||||||
MOZ_ASSERT(userAgent.Length() <= preallocatedLength);
|
MOZ_ASSERT(userAgent.Length() <= preallocatedLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP nsRFPService::GetSpoofedUserAgentService(bool aDesktopMode,
|
||||||
|
nsACString& aUserAgent) {
|
||||||
|
nsRFPService::GetSpoofedUserAgent(aUserAgent, aDesktopMode);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
nsCString nsRFPService::GetSpoofedJSLocale() { return "en-US"_ns; }
|
nsCString nsRFPService::GetSpoofedJSLocale() { return "en-US"_ns; }
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,12 @@
|
|||||||
// reason is that it is easy to detect the real platform. So there is no benefit
|
// reason is that it is easy to detect the real platform. So there is no benefit
|
||||||
// for hiding the platform: it only brings breakages, like keyboard shortcuts
|
// for hiding the platform: it only brings breakages, like keyboard shortcuts
|
||||||
// won't work in macOS if we spoof it as a Windows platform.
|
// won't work in macOS if we spoof it as a Windows platform.
|
||||||
|
|
||||||
|
// We use this value for Desktop mode in Android.
|
||||||
|
// That's why it is defined here, outside of
|
||||||
|
// the platform-specific definitions.
|
||||||
|
#define SPOOFED_UA_OS_OTHER "X11; Linux x86_64"
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
# define SPOOFED_UA_OS "Windows NT 10.0; Win64; x64"
|
# define SPOOFED_UA_OS "Windows NT 10.0; Win64; x64"
|
||||||
# define SPOOFED_APPVERSION "5.0 (Windows)"
|
# define SPOOFED_APPVERSION "5.0 (Windows)"
|
||||||
@@ -50,7 +56,7 @@
|
|||||||
#else
|
#else
|
||||||
// For Linux and other platforms, like BSDs, SunOS and etc, we will use Linux
|
// For Linux and other platforms, like BSDs, SunOS and etc, we will use Linux
|
||||||
// platform.
|
// platform.
|
||||||
# define SPOOFED_UA_OS "X11; Linux x86_64"
|
# define SPOOFED_UA_OS SPOOFED_UA_OS_OTHER
|
||||||
# define SPOOFED_APPVERSION "5.0 (X11)"
|
# define SPOOFED_APPVERSION "5.0 (X11)"
|
||||||
# define SPOOFED_OSCPU "Linux x86_64"
|
# define SPOOFED_OSCPU "Linux x86_64"
|
||||||
# define SPOOFED_MAX_TOUCH_POINTS 10
|
# define SPOOFED_MAX_TOUCH_POINTS 10
|
||||||
@@ -269,7 +275,8 @@ class nsRFPService final : public nsIObserver, public nsIRFPService {
|
|||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
// This method generates the spoofed value of User Agent.
|
// This method generates the spoofed value of User Agent.
|
||||||
static void GetSpoofedUserAgent(nsACString& userAgent);
|
static void GetSpoofedUserAgent(nsACString& userAgent,
|
||||||
|
bool aAndroidDesktopMode = false);
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user