Bug 1847990 - Canvas fingerprinting telemetry. r=timhuang,anti-tracking-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D189111
This commit is contained in:
Tom Schuster 2023-11-13 10:05:22 +00:00
Родитель ea0560e8fe
Коммит 864f258ff2
17 изменённых файлов: 235 добавлений и 66 удалений

Просмотреть файл

@ -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