зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1816064 - Part 1: Implement the session key for generating the random noise key for fingerprinting randomization. r=tjr
This patch implements the session key that will be used to generate the random noise for fingerprinting randomization. The session keys will live in the nsRFPService in the parent process only. nsRFPServices holds two session keys for normal and private windows respectively. The lifetime of normal sessino key is tie to the Firefox; it resets with Firefox restarts. For private session key, it resets when the private session ends. i.e. all private windows are closed. Differential Revision: https://phabricator.services.mozilla.com/D170603
This commit is contained in:
Родитель
4d752ebbe4
Коммит
3a6e536d7c
|
@ -12921,6 +12921,25 @@
|
|||
value: 0
|
||||
mirror: always
|
||||
|
||||
# Enable the fingerprinting randomization.
|
||||
- name: privacy.resistFingerprinting.randomization.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# Enable resetting the fingerprinting randomization key daily for normal windwos.
|
||||
- name: privacy.resistFingerprinting.randomization.daily_reset.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# Enable resetting the fingerprinting randomization key daily for private windwos.
|
||||
- name: privacy.resistFingerprinting.randomization.daily_reset.private.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
|
||||
# Anti-tracking permission expiration.
|
||||
- name: privacy.restrict3rdpartystorage.expiration
|
||||
type: uint32_t
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <utility>
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
#include "mozilla/ArrayIterator.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
|
@ -28,6 +29,7 @@
|
|||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/MacroForEach.h"
|
||||
#include "mozilla/OriginAttributes.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
@ -66,6 +68,7 @@
|
|||
#include "nsIGlobalObject.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIRandomGenerator.h"
|
||||
#include "nsIUserIdleService.h"
|
||||
#include "nsIXULAppInfo.h"
|
||||
|
||||
#include "nscore.h"
|
||||
|
@ -90,6 +93,8 @@ static mozilla::LazyLogModule gResistFingerprintingLog(
|
|||
#define RFP_JITTER_VALUE_PREF \
|
||||
"privacy.resistFingerprinting.reduceTimerPrecision.jitter"
|
||||
#define PROFILE_INITIALIZED_TOPIC "profile-initial-state"
|
||||
#define LAST_PB_SESSION_EXITED_TOPIC "last-pb-context-exited"
|
||||
#define STARTUP_COMPLETE_TOPIC "browser-delayed-startup-finished"
|
||||
|
||||
static constexpr uint32_t kVideoFramesPerSec = 30;
|
||||
static constexpr uint32_t kVideoDroppedRatio = 5;
|
||||
|
@ -97,6 +102,10 @@ static constexpr uint32_t kVideoDroppedRatio = 5;
|
|||
#define RFP_DEFAULT_SPOOFING_KEYBOARD_LANG KeyboardLang::EN
|
||||
#define RFP_DEFAULT_SPOOFING_KEYBOARD_REGION KeyboardRegion::US
|
||||
|
||||
// The number of seconds we will wait after receiving the idle-daily
|
||||
// notification before clearing the session keys.
|
||||
const uint32_t kIdleObserverTimeSec = 1;
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsRFPService, nsIObserver)
|
||||
|
||||
static StaticRefPtr<nsRFPService> sRFPService;
|
||||
|
@ -644,6 +653,14 @@ nsresult nsRFPService::Init() {
|
|||
rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (XRE_IsParentProcess()) {
|
||||
rv = obs->AddObserver(this, LAST_PB_SESSION_EXITED_TOPIC, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = obs->AddObserver(this, STARTUP_COMPLETE_TOPIC, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
rv = obs->AddObserver(this, PROFILE_INITIALIZED_TOPIC, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -734,7 +751,18 @@ void nsRFPService::StartShutdown() {
|
|||
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
if (XRE_IsParentProcess()) {
|
||||
obs->RemoveObserver(this, LAST_PB_SESSION_EXITED_TOPIC);
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIUserIdleService> idleService =
|
||||
do_GetService("@mozilla.org/widget/useridleservice;1");
|
||||
|
||||
if (idleService && XRE_IsParentProcess()) {
|
||||
idleService->RemoveIdleObserver(this, kIdleObserverTimeSec);
|
||||
}
|
||||
|
||||
Preferences::UnregisterCallbacks(nsRFPService::PrefChanged, gCallbackPrefs,
|
||||
this);
|
||||
}
|
||||
|
@ -1061,5 +1089,137 @@ nsRFPService::Observe(nsISupports* aObject, const char* aTopic,
|
|||
}
|
||||
#endif
|
||||
|
||||
if (strcmp(LAST_PB_SESSION_EXITED_TOPIC, aTopic) == 0) {
|
||||
// Clear the private session key when the private session ends so that we
|
||||
// can generate a new key for the new private session.
|
||||
ClearSessionKey(true);
|
||||
}
|
||||
|
||||
if (strcmp(STARTUP_COMPLETE_TOPIC, aTopic) == 0) {
|
||||
// Register the idle observer after the startup is finished.
|
||||
nsCOMPtr<nsIUserIdleService> idleService =
|
||||
do_GetService("@mozilla.org/widget/useridleservice;1");
|
||||
NS_ENSURE_TRUE(idleService, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsresult rv = idleService->AddIdleObserver(this, kIdleObserverTimeSec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
rv = obs->RemoveObserver(this, STARTUP_COMPLETE_TOPIC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (!strcmp(OBSERVER_TOPIC_IDLE_DAILY, aTopic)) {
|
||||
if (StaticPrefs::
|
||||
privacy_resistFingerprinting_randomization_daily_reset_enabled()) {
|
||||
ClearSessionKey(false);
|
||||
}
|
||||
|
||||
if (StaticPrefs::
|
||||
privacy_resistFingerprinting_randomization_daily_reset_private_enabled()) {
|
||||
ClearSessionKey(true);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsRFPService::EnsureSessionKey(bool aIsPrivate) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Info,
|
||||
("Ensure the session key for %s browsing session\n",
|
||||
aIsPrivate ? "private" : "normal"));
|
||||
|
||||
if (!StaticPrefs::privacy_resistFingerprinting_randomization_enabled()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
Maybe<nsID>& sessionKey =
|
||||
aIsPrivate ? mPrivateBrowsingSessionKey : mBrowsingSessionKey;
|
||||
|
||||
// The key has been generated, bail out earlier.
|
||||
if (sessionKey) {
|
||||
MOZ_LOG(
|
||||
gResistFingerprintingLog, LogLevel::Info,
|
||||
("The %s session key exists: %s\n", aIsPrivate ? "private" : "normal",
|
||||
sessionKey.ref().ToString().get()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
sessionKey.emplace(nsID::GenerateUUID());
|
||||
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Debug,
|
||||
("Generated %s session key: %s\n", aIsPrivate ? "private" : "normal",
|
||||
sessionKey.ref().ToString().get()));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void nsRFPService::ClearSessionKey(bool aIsPrivate) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
Maybe<nsID>& sessionKey =
|
||||
aIsPrivate ? mPrivateBrowsingSessionKey : mBrowsingSessionKey;
|
||||
|
||||
sessionKey.reset();
|
||||
}
|
||||
|
||||
// static
|
||||
Maybe<nsTArray<uint8_t>> nsRFPService::GenerateKey(nsIURI* aTopLevelURI,
|
||||
bool aIsPrivate) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(aTopLevelURI);
|
||||
|
||||
MOZ_LOG(gResistFingerprintingLog, LogLevel::Debug,
|
||||
("Generating %s randomization key for top-level URI: %s\n",
|
||||
aIsPrivate ? "private" : "normal",
|
||||
aTopLevelURI->GetSpecOrDefault().get()));
|
||||
|
||||
RefPtr<nsRFPService> service = GetOrCreate();
|
||||
|
||||
if (NS_FAILED(service->EnsureSessionKey(aIsPrivate))) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
const nsID& sessionKey = aIsPrivate
|
||||
? service->mPrivateBrowsingSessionKey.ref()
|
||||
: service->mBrowsingSessionKey.ref();
|
||||
|
||||
auto sessionKeyStr = sessionKey.ToString();
|
||||
|
||||
// Using the OriginAttributes to get the site from the top-level URI. The site
|
||||
// is composed of scheme, host, and port.
|
||||
OriginAttributes attrs;
|
||||
attrs.SetPartitionKey(aTopLevelURI);
|
||||
|
||||
// Generate the key by using the hMAC. The key is based on the session key and
|
||||
// the partitionKey, i.e. top-level site.
|
||||
HMAC hmac;
|
||||
|
||||
nsresult rv = hmac.Begin(
|
||||
SEC_OID_SHA256,
|
||||
Span(reinterpret_cast<const uint8_t*>(sessionKeyStr.get()), NSID_LENGTH));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 topLevelSite(attrs.mPartitionKey);
|
||||
rv = hmac.Update(reinterpret_cast<const uint8_t*>(topLevelSite.get()),
|
||||
topLevelSite.Length());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
Maybe<nsTArray<uint8_t>> key;
|
||||
key.emplace();
|
||||
|
||||
rv = hmac.End(key.ref());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "mozilla/BasicEvents.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsHashtablesFwd.h"
|
||||
#include "nsID.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsISupports.h"
|
||||
|
@ -227,6 +228,11 @@ class nsRFPService final : public nsIObserver {
|
|||
const WidgetKeyboardEvent* aKeyboardEvent,
|
||||
uint32_t& aOut);
|
||||
|
||||
// The method to generate the key for randomization. It can return nothing if
|
||||
// the session key is not available due to the randomization is disabled.
|
||||
static Maybe<nsTArray<uint8_t>> GenerateKey(nsIURI* aTopLevelURI,
|
||||
bool aIsPrivate);
|
||||
|
||||
private:
|
||||
nsresult Init();
|
||||
|
||||
|
@ -261,7 +267,24 @@ class nsRFPService final : public nsIObserver {
|
|||
|
||||
static void TypeToText(TimerPrecisionType aType, nsACString& aText);
|
||||
|
||||
// Generate the session key if it hasn't been generated.
|
||||
nsresult EnsureSessionKey(bool aIsPrivate);
|
||||
void ClearSessionKey(bool aIsPrivate);
|
||||
|
||||
nsCString mInitialTZValue;
|
||||
|
||||
// The keys that represent the browsing session. The lifetime of the key ties
|
||||
// to the browsing session. For normal windows, the key is generated when
|
||||
// loading the first http channel after the browser startup and persists until
|
||||
// the browser shuts down. For private windows, the key is generated when
|
||||
// opening a http channel on a private window and reset after all private
|
||||
// windows close, i.e. private browsing session ends.
|
||||
//
|
||||
// The key will be used to generate the randomization noise used to fiddle the
|
||||
// browser fingerprints. Note that this key lives and can only be accessed in
|
||||
// the parent process.
|
||||
Maybe<nsID> mBrowsingSessionKey;
|
||||
Maybe<nsID> mPrivateBrowsingSessionKey;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
Загрузка…
Ссылка в новой задаче