зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1847990 - Canvas fingerprinting telemetry. r=timhuang,anti-tracking-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D189111
This commit is contained in:
Родитель
ea0560e8fe
Коммит
864f258ff2
|
@ -16371,7 +16371,13 @@ void Document::RecordCanvasUsage(CanvasUsage& aUsage) {
|
|||
mCanvasUsage.AppendElement(aUsage);
|
||||
mLastCanvasUsage = now;
|
||||
|
||||
nsRFPService::MaybeReportCanvasFingerprinter(mCanvasUsage);
|
||||
nsCString originNoSuffix;
|
||||
if (NS_FAILED(NodePrincipal()->GetOriginNoSuffix(originNoSuffix))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRFPService::MaybeReportCanvasFingerprinter(mCanvasUsage, GetChannel(),
|
||||
originNoSuffix);
|
||||
}
|
||||
|
||||
WindowContext* Document::GetWindowContextForPageUseCounters() const {
|
||||
|
|
|
@ -3653,7 +3653,10 @@ void BrowserChild::NotifyContentBlockingEvent(
|
|||
const nsTArray<nsCString>& aTrackingFullHashes,
|
||||
const Maybe<
|
||||
mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason) {
|
||||
aReason,
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText) {
|
||||
if (!IPCOpen()) {
|
||||
return;
|
||||
}
|
||||
|
@ -3662,7 +3665,8 @@ void BrowserChild::NotifyContentBlockingEvent(
|
|||
if (NS_SUCCEEDED(PrepareRequestData(aChannel, requestData))) {
|
||||
Unused << SendNotifyContentBlockingEvent(
|
||||
aEvent, requestData, aBlocked, PromiseFlatCString(aTrackingOrigin),
|
||||
aTrackingFullHashes, aReason);
|
||||
aTrackingFullHashes, aReason, aCanvasFingerprinter,
|
||||
aCanvasFingerprinterKnownText);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -650,7 +650,10 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
|
|||
const nsTArray<nsCString>& aTrackingFullHashes,
|
||||
const Maybe<
|
||||
ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason);
|
||||
aReason,
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText);
|
||||
|
||||
protected:
|
||||
virtual ~BrowserChild();
|
||||
|
|
|
@ -3033,7 +3033,10 @@ mozilla::ipc::IPCResult BrowserParent::RecvNotifyContentBlockingEvent(
|
|||
nsTArray<nsCString>&& aTrackingFullHashes,
|
||||
const Maybe<
|
||||
mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason) {
|
||||
aReason,
|
||||
const Maybe<mozilla::ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool>& aCanvasFingerprinterKnownText) {
|
||||
RefPtr<BrowsingContext> bc = GetBrowsingContext();
|
||||
|
||||
if (!bc || bc->IsDiscarded()) {
|
||||
|
@ -3056,8 +3059,9 @@ mozilla::ipc::IPCResult BrowserParent::RecvNotifyContentBlockingEvent(
|
|||
aRequestData.requestURI(), aRequestData.originalRequestURI(),
|
||||
aRequestData.matchedList());
|
||||
|
||||
wgp->NotifyContentBlockingEvent(aEvent, request, aBlocked, aTrackingOrigin,
|
||||
aTrackingFullHashes, aReason);
|
||||
wgp->NotifyContentBlockingEvent(
|
||||
aEvent, request, aBlocked, aTrackingOrigin, aTrackingFullHashes, aReason,
|
||||
aCanvasFingerprinter, aCanvasFingerprinterKnownText);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
|
|
@ -303,7 +303,10 @@ class BrowserParent final : public PBrowserParent,
|
|||
const bool aBlocked, const nsACString& aTrackingOrigin,
|
||||
nsTArray<nsCString>&& aTrackingFullHashes,
|
||||
const Maybe<mozilla::ContentBlockingNotifier::
|
||||
StorageAccessPermissionGrantedReason>& aReason);
|
||||
StorageAccessPermissionGrantedReason>& aReason,
|
||||
const Maybe<mozilla::ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool>& aCanvasFingerprinterKnownText);
|
||||
|
||||
mozilla::ipc::IPCResult RecvNavigationFinished();
|
||||
|
||||
|
|
|
@ -101,6 +101,7 @@ using mozilla::ScrollFlags from "mozilla/PresShellForwards.h";
|
|||
using struct InputFormData from "mozilla/dom/SessionStoreMessageUtils.h";
|
||||
using struct CollectedInputDataValue from "mozilla/dom/SessionStoreMessageUtils.h";
|
||||
using mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason from "mozilla/ContentBlockingNotifier.h";
|
||||
using mozilla::ContentBlockingNotifier::CanvasFingerprinter from "mozilla/ContentBlockingNotifier.h";
|
||||
using mozilla::dom::CallerType from "mozilla/dom/BindingDeclarations.h";
|
||||
using mozilla::dom::EmbedderElementEventType from "mozilla/dom/TabMessageTypes.h";
|
||||
using mozilla::IntrinsicSize from "nsIFrame.h";
|
||||
|
@ -554,7 +555,9 @@ parent:
|
|||
async NotifyContentBlockingEvent(uint32_t aEvent, RequestData aRequestData,
|
||||
bool aBlocked, nsCString aTrackingOrigin,
|
||||
nsCString[] aTrackingFullHashes,
|
||||
StorageAccessPermissionGrantedReason? aReason);
|
||||
StorageAccessPermissionGrantedReason? aReason,
|
||||
CanvasFingerprinter? aCanvasFingerprinter,
|
||||
bool? aCanvasFingerprinterKnownText);
|
||||
|
||||
async NavigationFinished();
|
||||
|
||||
|
|
|
@ -547,7 +547,10 @@ void WindowGlobalParent::NotifyContentBlockingEvent(
|
|||
const nsACString& aTrackingOrigin,
|
||||
const nsTArray<nsCString>& aTrackingFullHashes,
|
||||
const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason) {
|
||||
aReason,
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
DebugOnly<bool> isCookiesBlocked =
|
||||
aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
|
||||
|
@ -565,7 +568,8 @@ void WindowGlobalParent::NotifyContentBlockingEvent(
|
|||
}
|
||||
|
||||
Maybe<uint32_t> event = GetContentBlockingLog()->RecordLogParent(
|
||||
aTrackingOrigin, aEvent, aBlocked, aReason, aTrackingFullHashes);
|
||||
aTrackingOrigin, aEvent, aBlocked, aReason, aTrackingFullHashes,
|
||||
aCanvasFingerprinter, aCanvasFingerprinterKnownText);
|
||||
|
||||
// Notify the OnContentBlockingEvent if necessary.
|
||||
if (event) {
|
||||
|
@ -1501,6 +1505,8 @@ void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
|
|||
|
||||
if (mDocumentURI && (net::SchemeIsHTTP(mDocumentURI) ||
|
||||
net::SchemeIsHTTPS(mDocumentURI))) {
|
||||
GetContentBlockingLog()->ReportCanvasFingerprintingLog(
|
||||
DocumentPrincipal());
|
||||
GetContentBlockingLog()->ReportEmailTrackingLog(DocumentPrincipal());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,7 +179,10 @@ class WindowGlobalParent final : public WindowContext,
|
|||
const nsTArray<nsCString>& aTrackingFullHashes,
|
||||
const Maybe<
|
||||
ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason = Nothing());
|
||||
aReason,
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText);
|
||||
|
||||
ContentBlockingLog* GetContentBlockingLog() { return &mContentBlockingLog; }
|
||||
|
||||
|
|
|
@ -54,6 +54,14 @@ struct ParamTraits<nsILoadInfo::StoragePermissionState>
|
|||
nsILoadInfo::StoragePermissionState,
|
||||
nsILoadInfo::StoragePermissionState::NoStoragePermission,
|
||||
nsILoadInfo::StoragePermissionState::StoragePermissionAllowListed> {};
|
||||
|
||||
// ContentBlockingNotifier::CanvasFingerprinter over IPC.
|
||||
template <>
|
||||
struct ParamTraits<mozilla::ContentBlockingNotifier::CanvasFingerprinter>
|
||||
: public ContiguousEnumSerializerInclusive<
|
||||
mozilla::ContentBlockingNotifier::CanvasFingerprinter,
|
||||
mozilla::ContentBlockingNotifier::CanvasFingerprinter::eFingerprintJS,
|
||||
mozilla::ContentBlockingNotifier::CanvasFingerprinter::eMaybe> {};
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_antitrackingipcutils_h
|
||||
|
|
|
@ -49,7 +49,10 @@ Maybe<uint32_t> ContentBlockingLog::RecordLogParent(
|
|||
const nsACString& aOrigin, uint32_t aType, bool aBlocked,
|
||||
const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason,
|
||||
const nsTArray<nsCString>& aTrackingFullHashes) {
|
||||
const nsTArray<nsCString>& aTrackingFullHashes,
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
uint32_t events = GetContentBlockingEventsInLog();
|
||||
|
@ -110,6 +113,11 @@ Maybe<uint32_t> ContentBlockingLog::RecordLogParent(
|
|||
RecordLogInternal(aOrigin, aType, blockedValue);
|
||||
break;
|
||||
|
||||
case nsIWebProgressListener::STATE_ALLOWED_CANVAS_FINGERPRINTING:
|
||||
RecordLogInternal(aOrigin, aType, blockedValue, Nothing(), {},
|
||||
aCanvasFingerprinter, aCanvasFingerprinterKnownText);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
|
||||
break;
|
||||
|
@ -167,6 +175,58 @@ void ContentBlockingLog::ReportLog(nsIPrincipal* aFirstPartyPrincipal) {
|
|||
trackingDBService->RecordContentBlockingLog(Stringify());
|
||||
}
|
||||
|
||||
void ContentBlockingLog::ReportCanvasFingerprintingLog(
|
||||
nsIPrincipal* aFirstPartyPrincipal) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aFirstPartyPrincipal);
|
||||
|
||||
// We don't need to report if the first party is not a content.
|
||||
if (!BasePrincipal::Cast(aFirstPartyPrincipal)->IsContentPrincipal()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasCanvasFingerprinter = false;
|
||||
bool canvasFingerprinterKnownText = false;
|
||||
Maybe<ContentBlockingNotifier::CanvasFingerprinter> canvasFingerprinter;
|
||||
for (const auto& originEntry : mLog) {
|
||||
if (!originEntry.mData) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& logEntry : Reversed(originEntry.mData->mLogs)) {
|
||||
if (logEntry.mType !=
|
||||
nsIWebProgressListener::STATE_ALLOWED_CANVAS_FINGERPRINTING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Select the log entry with the highest fingerprinting likelihood,
|
||||
// that primarily means preferring those with a FingerprinterKnownText.
|
||||
if (!hasCanvasFingerprinter ||
|
||||
(!canvasFingerprinterKnownText &&
|
||||
*logEntry.mCanvasFingerprinterKnownText) ||
|
||||
(!canvasFingerprinterKnownText && canvasFingerprinter.isNothing() &&
|
||||
logEntry.mCanvasFingerprinter.isSome())) {
|
||||
hasCanvasFingerprinter = true;
|
||||
canvasFingerprinterKnownText = *logEntry.mCanvasFingerprinterKnownText;
|
||||
canvasFingerprinter = logEntry.mCanvasFingerprinter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasCanvasFingerprinter) {
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_FINGERPRINTING_PER_TAB,
|
||||
"unknown"_ns, 0);
|
||||
} else {
|
||||
int32_t fingerprinter =
|
||||
canvasFingerprinter.isSome() ? (*canvasFingerprinter + 1) : 0;
|
||||
Telemetry::Accumulate(
|
||||
Telemetry::CANVAS_FINGERPRINTING_PER_TAB,
|
||||
canvasFingerprinterKnownText ? "known_text"_ns : "unknown"_ns,
|
||||
fingerprinter);
|
||||
}
|
||||
}
|
||||
|
||||
void ContentBlockingLog::ReportEmailTrackingLog(
|
||||
nsIPrincipal* aFirstPartyPrincipal) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
|
|
@ -33,6 +33,8 @@ class ContentBlockingLog final {
|
|||
Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>
|
||||
mReason;
|
||||
nsTArray<nsCString> mTrackingFullHashes;
|
||||
Maybe<ContentBlockingNotifier::CanvasFingerprinter> mCanvasFingerprinter;
|
||||
Maybe<bool> mCanvasFingerprinterKnownText;
|
||||
};
|
||||
|
||||
struct OriginDataEntry {
|
||||
|
@ -84,7 +86,10 @@ class ContentBlockingLog final {
|
|||
const Maybe<
|
||||
ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason,
|
||||
const nsTArray<nsCString>& aTrackingFullHashes);
|
||||
const nsTArray<nsCString>& aTrackingFullHashes,
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText);
|
||||
|
||||
void RecordLog(
|
||||
const nsACString& aOrigin, uint32_t aType, bool aBlocked,
|
||||
|
@ -96,6 +101,7 @@ class ContentBlockingLog final {
|
|||
}
|
||||
|
||||
void ReportLog(nsIPrincipal* aFirstPartyPrincipal);
|
||||
void ReportCanvasFingerprintingLog(nsIPrincipal* aFirstPartyPrincipal);
|
||||
void ReportEmailTrackingLog(nsIPrincipal* aFirstPartyPrincipal);
|
||||
|
||||
nsAutoCString Stringify() {
|
||||
|
@ -244,7 +250,10 @@ class ContentBlockingLog final {
|
|||
const Maybe<
|
||||
ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason = Nothing(),
|
||||
const nsTArray<nsCString>& aTrackingFullHashes = nsTArray<nsCString>()) {
|
||||
const nsTArray<nsCString>& aTrackingFullHashes = nsTArray<nsCString>(),
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
|
||||
aCanvasFingerprinter = Nothing(),
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText = Nothing()) {
|
||||
DebugOnly<bool> isCookiesBlockedTracker =
|
||||
aType == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
|
||||
aType == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
|
||||
|
@ -267,7 +276,10 @@ class ContentBlockingLog final {
|
|||
}
|
||||
if (!entry.mData->mLogs.IsEmpty()) {
|
||||
auto& last = entry.mData->mLogs.LastElement();
|
||||
if (last.mType == aType && last.mBlocked == aBlocked) {
|
||||
if (last.mType == aType && last.mBlocked == aBlocked &&
|
||||
last.mCanvasFingerprinter == aCanvasFingerprinter &&
|
||||
last.mCanvasFingerprinterKnownText ==
|
||||
aCanvasFingerprinterKnownText) {
|
||||
++last.mRepeatCount;
|
||||
// Don't record recorded events. This helps compress our log.
|
||||
// We don't care about if the the reason is the same, just keep the
|
||||
|
@ -290,7 +302,8 @@ class ContentBlockingLog final {
|
|||
entry.mData->mLogs.RemoveElementAt(0);
|
||||
}
|
||||
entry.mData->mLogs.AppendElement(
|
||||
LogEntry{aType, 1u, aBlocked, aReason, aTrackingFullHashes.Clone()});
|
||||
LogEntry{aType, 1u, aBlocked, aReason, aTrackingFullHashes.Clone(),
|
||||
aCanvasFingerprinter, aCanvasFingerprinterKnownText});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -320,12 +333,13 @@ class ContentBlockingLog final {
|
|||
entry->mData->mHasSocialTrackerCookiesLoaded.emplace(aBlocked);
|
||||
} else {
|
||||
entry->mData->mLogs.AppendElement(
|
||||
LogEntry{aType, 1u, aBlocked, aReason, aTrackingFullHashes.Clone()});
|
||||
LogEntry{aType, 1u, aBlocked, aReason, aTrackingFullHashes.Clone(),
|
||||
aCanvasFingerprinter, aCanvasFingerprinterKnownText});
|
||||
}
|
||||
}
|
||||
|
||||
bool RecordLogEntryInCustomField(uint32_t aType, OriginEntry& aEntry,
|
||||
bool aBlocked) {
|
||||
bool RecordLogEntryInCustomField(
|
||||
uint32_t aType, OriginEntry& aEntry, bool aBlocked) {
|
||||
if (aType ==
|
||||
nsIWebProgressListener::STATE_LOADED_LEVEL_1_TRACKING_CONTENT) {
|
||||
aEntry.mData->mHasLevel1TrackingContentLoaded = aBlocked;
|
||||
|
|
|
@ -336,7 +336,10 @@ void NotifyEventInChild(
|
|||
nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
|
||||
const nsACString& aTrackingOrigin,
|
||||
const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason) {
|
||||
aReason,
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText) {
|
||||
MOZ_ASSERT(XRE_IsContentProcess());
|
||||
|
||||
// We don't need to find the top-level window here because the
|
||||
|
@ -365,9 +368,10 @@ void NotifyEventInChild(
|
|||
trackingFullHashes);
|
||||
}
|
||||
|
||||
browserChild->NotifyContentBlockingEvent(aRejectedReason, aTrackingChannel,
|
||||
aBlocked, aTrackingOrigin,
|
||||
trackingFullHashes, aReason);
|
||||
browserChild->NotifyContentBlockingEvent(
|
||||
aRejectedReason, aTrackingChannel, aBlocked, aTrackingOrigin,
|
||||
trackingFullHashes, aReason, aCanvasFingerprinter,
|
||||
aCanvasFingerprinterKnownText);
|
||||
}
|
||||
|
||||
// Update the ContentBlockingLog of the top-level WindowGlobalParent of
|
||||
|
@ -376,7 +380,10 @@ void NotifyEventInParent(
|
|||
nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
|
||||
const nsACString& aTrackingOrigin,
|
||||
const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
|
||||
aReason) {
|
||||
aReason,
|
||||
const Maybe<ContentBlockingNotifier::CanvasFingerprinter>
|
||||
aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aTrackingChannel->LoadInfo();
|
||||
|
@ -402,7 +409,9 @@ void NotifyEventInParent(
|
|||
}
|
||||
|
||||
wgp->NotifyContentBlockingEvent(aRejectedReason, aTrackingChannel, aBlocked,
|
||||
aTrackingOrigin, trackingFullHashes, aReason);
|
||||
aTrackingOrigin, trackingFullHashes, aReason,
|
||||
aCanvasFingerprinter,
|
||||
aCanvasFingerprinterKnownText);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -565,12 +574,16 @@ void ContentBlockingNotifier::OnEvent(nsIChannel* aTrackingChannel,
|
|||
void ContentBlockingNotifier::OnEvent(
|
||||
nsIChannel* aTrackingChannel, bool aBlocked, uint32_t aRejectedReason,
|
||||
const nsACString& aTrackingOrigin,
|
||||
const Maybe<StorageAccessPermissionGrantedReason>& aReason) {
|
||||
const Maybe<StorageAccessPermissionGrantedReason>& aReason,
|
||||
const Maybe<CanvasFingerprinter>& aCanvasFingerprinter,
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
NotifyEventInParent(aTrackingChannel, aBlocked, aRejectedReason,
|
||||
aTrackingOrigin, aReason);
|
||||
aTrackingOrigin, aReason, aCanvasFingerprinter,
|
||||
aCanvasFingerprinterKnownText);
|
||||
} else {
|
||||
NotifyEventInChild(aTrackingChannel, aBlocked, aRejectedReason,
|
||||
aTrackingOrigin, aReason);
|
||||
aTrackingOrigin, aReason, aCanvasFingerprinter,
|
||||
aCanvasFingerprinterKnownText);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,26 @@ class ContentBlockingNotifier final {
|
|||
ePrivilegeStorageAccessForOriginAPI,
|
||||
};
|
||||
|
||||
// We try to classify observed canvas fingerprinting scripts into different classes,
|
||||
// but we don't usually know the source/vendor of those scripts.
|
||||
// The classification is based on a behavioral analysis, based on type of canvas,
|
||||
// the extracted (e.g. toDataURL) size, the usage of functions like fillText etc.
|
||||
// See `nsRFPService::MaybeReportCanvasFingerprinter` for the classification heuristic.
|
||||
enum CanvasFingerprinter {
|
||||
// Suspected fingerprint.com (FingerprintJS)
|
||||
eFingerprintJS,
|
||||
// Suspected Akamai fingerprinter
|
||||
eAkamai,
|
||||
// Unknown but distinct types of fingerprinters
|
||||
eVariant1,
|
||||
eVariant2,
|
||||
eVariant3,
|
||||
eVariant4,
|
||||
// This just indicates that more than one canvas was extracted and is a
|
||||
// very weak signal.
|
||||
eMaybe
|
||||
};
|
||||
|
||||
// This method can be called on the parent process or on the content process.
|
||||
// The notification is propagated to the child channel if aChannel is a parent
|
||||
// channel proxy.
|
||||
|
@ -62,7 +82,10 @@ class ContentBlockingNotifier final {
|
|||
static void OnEvent(
|
||||
nsIChannel* aChannel, bool aBlocked, uint32_t aRejectedReason,
|
||||
const nsACString& aTrackingOrigin,
|
||||
const Maybe<StorageAccessPermissionGrantedReason>& aReason = Nothing());
|
||||
const ::mozilla::Maybe<StorageAccessPermissionGrantedReason>& aReason =
|
||||
Nothing(),
|
||||
const Maybe<CanvasFingerprinter>& aCanvasFingerprinter = Nothing(),
|
||||
const Maybe<bool> aCanvasFingerprinterKnownText = Nothing());
|
||||
|
||||
static void ReportUnblockingToConsole(
|
||||
dom::BrowsingContext* aBrowsingContext, const nsAString& aTrackingOrigin,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/ContentBlockingNotifier.h"
|
||||
#include "mozilla/glean/GleanMetrics.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/HelperMacros.h"
|
||||
|
@ -40,6 +41,7 @@
|
|||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/BrowsingContext.h"
|
||||
#include "mozilla/dom/CanvasRenderingContextHelper.h"
|
||||
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
@ -78,6 +80,7 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsIRandomGenerator.h"
|
||||
#include "nsIUserIdleService.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsIXULAppInfo.h"
|
||||
|
||||
#include "nscore.h"
|
||||
|
@ -1396,10 +1399,13 @@ nsresult nsRFPService::RandomizePixels(nsICookieJarSettings* aCookieJarSettings,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
|
||||
/* static */ void nsRFPService::MaybeReportCanvasFingerprinter(
|
||||
nsTArray<CanvasUsage>& aUses) {
|
||||
nsTArray<CanvasUsage>& aUses, nsIChannel* aChannel,
|
||||
nsACString& aOriginNoSuffix) {
|
||||
if (!aChannel) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t extractedWebGL = 0;
|
||||
bool seenExtractedWebGL_300x150 = false;
|
||||
|
||||
|
@ -1446,52 +1452,46 @@ nsresult nsRFPService::RandomizePixels(nsICookieJarSettings* aCookieJarSettings,
|
|||
}
|
||||
}
|
||||
|
||||
Maybe<ContentBlockingNotifier::CanvasFingerprinter> fingerprinter;
|
||||
if (seenExtractedWebGL_300x150 && seenExtracted2D_240x60 &&
|
||||
seenExtracted2D_122x110) {
|
||||
// pintrest.com seems use fingerprintJs, but does not use WebGL
|
||||
std::cerr << "[DEBUG][FINGERPRINTER] FingerprintJS commercial "
|
||||
"fingerprinting detected."
|
||||
<< std::endl;
|
||||
fingerprinter =
|
||||
Some(ContentBlockingNotifier::CanvasFingerprinter::eFingerprintJS);
|
||||
} else if (seenExtractedWebGL_300x150 && seenExtracted2D_280x60 &&
|
||||
seenExtracted2D_16x16) {
|
||||
std::cerr << "[DEBUG][FINGERPRINTER] Akamai fingerprinting detected."
|
||||
<< std::endl;
|
||||
fingerprinter = Some(ContentBlockingNotifier::CanvasFingerprinter::eAkamai);
|
||||
} else if (seenExtractedWebGL_300x150 && extracted2D > 0 &&
|
||||
(featureUsage & CanvasFeatureUsage::SetFont)) {
|
||||
std::cerr << "[DEBUG][HIGHLY LIKELY FINGERPRINTER] Unknown "
|
||||
"fingerprinting detected (Variant 1)."
|
||||
<< std::endl;
|
||||
} else if (extractedWebGL > 0 && extracted2D > 1 &&
|
||||
seenExtracted2D_860x6 /* added */) {
|
||||
// manage.wix.com / zoominfo.com
|
||||
// Uses:
|
||||
// width: 650 height: 12 type: Canvas2D (12 !!!)
|
||||
// width: 860 height: 6 type: Canvas2D
|
||||
// width: 2000 height: 200 type: WebGL1
|
||||
std::cerr << "[DEBUG][HIGHLY LIKELY FINGERPRINTER] Unknown "
|
||||
"fingerprinting detected (Variant 2)."
|
||||
<< std::endl;
|
||||
fingerprinter =
|
||||
Some(ContentBlockingNotifier::CanvasFingerprinter::eVariant1);
|
||||
} else if (extractedWebGL > 0 && extracted2D > 1 && seenExtracted2D_860x6) {
|
||||
fingerprinter =
|
||||
Some(ContentBlockingNotifier::CanvasFingerprinter::eVariant2);
|
||||
} else if (extractedOther > 0 && (extractedWebGL > 0 || extracted2D > 0)) {
|
||||
// This was never hit in the top 1k sites
|
||||
std::cerr << "[DEBUG][LIKELY FINGERPRINTER] Likely fingerprinting "
|
||||
"detected (Variant 3)."
|
||||
<< std::endl;
|
||||
fingerprinter =
|
||||
Some(ContentBlockingNotifier::CanvasFingerprinter::eVariant3);
|
||||
} else if (extracted2D > 0 && (featureUsage & CanvasFeatureUsage::SetFont) &&
|
||||
(featureUsage &
|
||||
(CanvasFeatureUsage::FillRect | CanvasFeatureUsage::LineTo |
|
||||
CanvasFeatureUsage::Stroke))) {
|
||||
std::cerr << "[DEBUG][LIKELY FINGERPRINTER] Likely fingerprinting "
|
||||
"detected (Variant 4)."
|
||||
<< std::endl;
|
||||
fingerprinter =
|
||||
Some(ContentBlockingNotifier::CanvasFingerprinter::eVariant4);
|
||||
} else if (extractedOther + extractedWebGL + extracted2D > 1) {
|
||||
// This I added primarily to not miss anything, but it can cause false
|
||||
// positives.
|
||||
std::cerr << "[DEBUG][MAYBE FINGERPRINTER] Potential fingerprinting "
|
||||
"detected."
|
||||
<< std::endl;
|
||||
} else {
|
||||
std::cerr << "[DEBUG][NO FINGERPRINTER]" << std::endl;
|
||||
fingerprinter = Some(ContentBlockingNotifier::CanvasFingerprinter::eMaybe);
|
||||
}
|
||||
|
||||
if (!(featureUsage & CanvasFeatureUsage::KnownFingerprintText) &&
|
||||
fingerprinter.isNothing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ContentBlockingNotifier::OnEvent(
|
||||
aChannel, false,
|
||||
nsIWebProgressListener::STATE_ALLOWED_CANVAS_FINGERPRINTING,
|
||||
aOriginNoSuffix, Nothing(), fingerprinter,
|
||||
Some(featureUsage & CanvasFeatureUsage::KnownFingerprintText));
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
|
|
@ -74,7 +74,7 @@ class WidgetKeyboardEvent;
|
|||
namespace dom {
|
||||
class Document;
|
||||
enum class CanvasContextType : uint8_t;
|
||||
}
|
||||
} // namespace dom
|
||||
|
||||
enum KeyboardLang { EN = 0x01 };
|
||||
|
||||
|
@ -349,7 +349,9 @@ class nsRFPService final : public nsIObserver, public nsIRFPService {
|
|||
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
static void MaybeReportCanvasFingerprinter(nsTArray<CanvasUsage>& aUses);
|
||||
static void MaybeReportCanvasFingerprinter(nsTArray<CanvasUsage>& aUses,
|
||||
nsIChannel* aChannel,
|
||||
nsACString& aOriginNoSuffix);
|
||||
|
||||
private:
|
||||
nsresult Init();
|
||||
|
|
|
@ -2479,6 +2479,19 @@
|
|||
"description": "WebGL2 creation success",
|
||||
"bug_numbers": [1247327]
|
||||
},
|
||||
"CANVAS_FINGERPRINTING_PER_TAB": {
|
||||
"record_in_processes": ["main"],
|
||||
"products": ["firefox", "fennec"],
|
||||
"alert_emails": ["seceng-telemetry@mozilla.com"],
|
||||
"expires_in_version": "126",
|
||||
"keyed": true,
|
||||
"keys": ["known_text", "unknown"],
|
||||
"kind": "enumerated",
|
||||
"n_values": 8,
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"description": "Type of canvas fingerprinter detected (keyed by known_fingerprinting_text or unknown), 0 = none",
|
||||
"bug_numbers": [1847990]
|
||||
},
|
||||
"TOTAL_CONTENT_PAGE_LOAD_TIME": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"products": ["firefox", "fennec", "geckoview_streaming"],
|
||||
|
|
|
@ -340,6 +340,9 @@ interface nsIWebProgressListener : nsISupports
|
|||
*
|
||||
* STATE_LOADED_EMAILTRACKING_LEVEL_2_CONTENT
|
||||
* EmailTracking content from the Disconnect level 2 has been loaded.
|
||||
*
|
||||
* STATE_ALLOWED_CANVAS_FINGERPRINTING
|
||||
* A potential attempt to fingerprint using the canvas API was observed.
|
||||
*/
|
||||
const unsigned long STATE_BLOCKED_TRACKING_CONTENT = 0x00001000;
|
||||
const unsigned long STATE_LOADED_LEVEL_1_TRACKING_CONTENT = 0x00002000;
|
||||
|
@ -367,6 +370,7 @@ interface nsIWebProgressListener : nsISupports
|
|||
const unsigned long STATE_BLOCKED_EMAILTRACKING_CONTENT = 0x00400000;
|
||||
const unsigned long STATE_LOADED_EMAILTRACKING_LEVEL_1_CONTENT = 0x00800000;
|
||||
const unsigned long STATE_LOADED_EMAILTRACKING_LEVEL_2_CONTENT = 0x00000100;
|
||||
const unsigned long STATE_ALLOWED_CANVAS_FINGERPRINTING = 0x02000000;
|
||||
|
||||
/**
|
||||
* Flags for HTTPS-Only and HTTPS-First Mode upgrades
|
||||
|
|
Загрузка…
Ссылка в новой задаче