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,
|
||||
const Optional<bool>& aIsPBM) {
|
||||
RFPTarget target;
|
||||
#define JSRFP_TARGET_TO_RFP_TARGET(rfptarget) \
|
||||
case JSRFPTarget::rfptarget: \
|
||||
target = RFPTarget::rfptarget; \
|
||||
break;
|
||||
switch (aTarget) {
|
||||
case JSRFPTarget::RoundWindowSize:
|
||||
target = RFPTarget::RoundWindowSize;
|
||||
break;
|
||||
case JSRFPTarget::SiteSpecificZoom:
|
||||
target = RFPTarget::SiteSpecificZoom;
|
||||
break;
|
||||
case JSRFPTarget::CSSPrefersColorScheme:
|
||||
target = RFPTarget::CSSPrefersColorScheme;
|
||||
break;
|
||||
case JSRFPTarget::JSLocalePrompt:
|
||||
target = RFPTarget::JSLocalePrompt;
|
||||
break;
|
||||
JSRFP_TARGET_TO_RFP_TARGET(RoundWindowSize);
|
||||
JSRFP_TARGET_TO_RFP_TARGET(SiteSpecificZoom);
|
||||
JSRFP_TARGET_TO_RFP_TARGET(CSSPrefersColorScheme);
|
||||
JSRFP_TARGET_TO_RFP_TARGET(JSLocalePrompt);
|
||||
JSRFP_TARGET_TO_RFP_TARGET(HttpUserAgent);
|
||||
default:
|
||||
MOZ_CRASH("Unhandled JSRFPTarget enum value");
|
||||
}
|
||||
#undef JSRFP_TARGET_TO_RFP_TARGET
|
||||
|
||||
bool isPBM = false;
|
||||
if (aIsPBM.WasPassed()) {
|
||||
|
||||
@@ -1161,6 +1161,7 @@ enum JSRFPTarget {
|
||||
"SiteSpecificZoom",
|
||||
"CSSPrefersColorScheme",
|
||||
"JSLocalePrompt",
|
||||
"HttpUserAgent",
|
||||
};
|
||||
|
||||
#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 {
|
||||
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.filters.MediumTest
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.hamcrest.Matchers.nullValue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule.Setting
|
||||
@@ -130,23 +129,14 @@ class RuntimeSettingsDefaultsTest : BaseSessionTest() {
|
||||
|
||||
@Test
|
||||
fun fingerprintProtectionsDefaults() {
|
||||
val geckoRuntimeSettings = sessionRule.runtime.settings
|
||||
val fingerprintingProtection =
|
||||
(sessionRule.getPrefs("privacy.fingerprintingProtection").get(0)) as Boolean
|
||||
val fingerprintingProtectionPrivateBrowsing =
|
||||
(sessionRule.getPrefs("privacy.fingerprintingProtection.pbmode").get(0)) as Boolean
|
||||
|
||||
assertThat(
|
||||
"Suspected Fingerprint Protection runtime settings should return null by default in normal tabs",
|
||||
geckoRuntimeSettings.fingerprintingProtection,
|
||||
nullValue(),
|
||||
)
|
||||
|
||||
assertThat(
|
||||
"Suspected Fingerprint Protection runtime settings should return null by default in private tabs",
|
||||
geckoRuntimeSettings.fingerprintingProtectionPrivateBrowsing,
|
||||
nullValue(),
|
||||
)
|
||||
// Removing two of these defaults tests because depending on the test order,
|
||||
// NavigationDelegateTest.desktopModeRFP can change the value and hence cause default
|
||||
// verification failure
|
||||
|
||||
assertThat(
|
||||
"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 () {
|
||||
if (ChromeUtils.shouldResistFingerprinting("HttpUserAgent", null)) {
|
||||
return Services.rfp.getSpoofedUserAgent(false);
|
||||
}
|
||||
|
||||
return Cc["@mozilla.org/network/protocol;1?name=http"].getService(
|
||||
Ci.nsIHttpProtocolHandler
|
||||
).userAgent;
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "DESKTOP_USER_AGENT", function () {
|
||||
if (ChromeUtils.shouldResistFingerprinting("HttpUserAgent", null)) {
|
||||
return Services.rfp.getSpoofedUserAgent(true);
|
||||
}
|
||||
|
||||
return lazy.MOBILE_USER_AGENT.replace(
|
||||
/Android \d.+?; [a-zA-Z]+/,
|
||||
"X11; Linux x86_64"
|
||||
|
||||
@@ -79,6 +79,9 @@ interface nsIRFPService : nsISupports
|
||||
*/
|
||||
void cleanRandomKeyByOriginAttributesPattern(in AString aPattern);
|
||||
|
||||
[binaryname(GetSpoofedUserAgentService)]
|
||||
ACString getSpoofedUserAgent(in boolean aDesktopMode);
|
||||
|
||||
/**
|
||||
* Test function for generating and return the fingerprinting randomization
|
||||
* key for the given channel.
|
||||
|
||||
@@ -1000,7 +1000,8 @@ uint32_t nsRFPService::GetSpoofedPresentedFrames(double aTime, uint32_t aWidth,
|
||||
// User-Agent/Version Stuff
|
||||
|
||||
/* static */
|
||||
void nsRFPService::GetSpoofedUserAgent(nsACString& userAgent) {
|
||||
void nsRFPService::GetSpoofedUserAgent(nsACString& userAgent,
|
||||
bool aAndroidDesktopMode /* = false */) {
|
||||
// This function generates the spoofed value of User Agent.
|
||||
// We spoof the values of the platform and Firefox version, which could be
|
||||
// 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"
|
||||
userAgent.AssignLiteral("Mozilla/5.0 (");
|
||||
if (aAndroidDesktopMode) {
|
||||
userAgent.AppendLiteral(SPOOFED_UA_OS_OTHER);
|
||||
} else {
|
||||
userAgent.AppendLiteral(SPOOFED_UA_OS);
|
||||
}
|
||||
userAgent.AppendLiteral("; rv:" MOZILLA_UAVERSION ") Gecko/");
|
||||
#if defined(ANDROID)
|
||||
if (aAndroidDesktopMode) {
|
||||
userAgent.AppendLiteral(LEGACY_UA_GECKO_TRAIL);
|
||||
} else {
|
||||
userAgent.AppendLiteral(MOZILLA_UAVERSION);
|
||||
}
|
||||
#else
|
||||
userAgent.AppendLiteral(LEGACY_UA_GECKO_TRAIL);
|
||||
#endif
|
||||
@@ -1028,6 +1037,12 @@ void nsRFPService::GetSpoofedUserAgent(nsACString& userAgent) {
|
||||
MOZ_ASSERT(userAgent.Length() <= preallocatedLength);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP nsRFPService::GetSpoofedUserAgentService(bool aDesktopMode,
|
||||
nsACString& aUserAgent) {
|
||||
nsRFPService::GetSpoofedUserAgent(aUserAgent, aDesktopMode);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
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
|
||||
// for hiding the platform: it only brings breakages, like keyboard shortcuts
|
||||
// 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
|
||||
# define SPOOFED_UA_OS "Windows NT 10.0; Win64; x64"
|
||||
# define SPOOFED_APPVERSION "5.0 (Windows)"
|
||||
@@ -50,7 +56,7 @@
|
||||
#else
|
||||
// For Linux and other platforms, like BSDs, SunOS and etc, we will use Linux
|
||||
// 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_OSCPU "Linux x86_64"
|
||||
# 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.
|
||||
static void GetSpoofedUserAgent(nsACString& userAgent);
|
||||
static void GetSpoofedUserAgent(nsACString& userAgent,
|
||||
bool aAndroidDesktopMode = false);
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user