CVE-2024-27830
Overview
Background
- Canvas fingerprinting
- Deriving a stable identifier from subtle per-device/OS differences in canvas/OffscreenCanvas pixel output.
- Noise injection (Advanced Privacy Protections)
- In Private Browsing WebKit perturbs canvas readback using a per-origin hash salt so the pixels cannot be used as a fingerprint.
- Shared/service workers and remote Page
- These workers run in a separate, potentially-remote Page; state like the noise salt must be explicitly plumbed to them or it is absent.
- WorkerParameters plumbing
- The mechanism that carries context state (including privacy flags/salt) from the originating document to a worker global scope.
Root Cause Analysis
This fixes a canvas anti-fingerprinting gap: in Private Browsing, pixel-readback noise injection was not applied to OffscreenCanvas used inside shared workers and service workers. In Private Browsing (Advanced Privacy Protections), each ScriptExecutionContext carries a per-origin noise-injection hash salt and AdvancedPrivacyProtections flags, sourced from the document loader, and these are used to perturb pixels read back from canvas/OffscreenCanvas so the output cannot be used as a stable fingerprint. For dedicated workers the salt/flags were already plumbed via WorkerParameters to the WorkerGlobalScope, so OffscreenCanvas there got noise. But shared workers and service workers run their OffscreenCanvas code in a separate, potentially-remote Page that had neither the hash salt nor the AdvancedPrivacyProtections flags, so readback in those contexts received NO noise — letting a site fingerprint the device/OS via OffscreenCanvas from a shared/service worker, bypassing the Private Browsing protection.
The fix extends the AdvancedPrivacyProtections plumbing to those two worker types: it adds an override point (ScriptExecutionContext::advancedPrivacyProtections, implemented by Document via the top document’s loader and by workers via passed-in parameters), threads the flags through WorkerScriptLoader / WorkerInitializationData / WorkerParameters, and lets Page::setupForRemoteWorker receive the privacy protections when initializing the remote Page.
The restored invariant is that canvas/OffscreenCanvas readback noise is applied in all worker types (dedicated, shared, service) when Private Browsing protections are active. The test verifies noise injection for OffscreenCanvas in a shared worker.
Attack Path
- Enter Private Browsing The user browses privately, expecting canvas readback to be noised against fingerprinting.
- Use OffscreenCanvas in a shared/service worker The page renders to an OffscreenCanvas inside a shared or service worker, whose remote Page lacked the salt and privacy flags.
- Read back un-noised pixels Because noise injection was not applied in that context, the readback returns the true, un-perturbed pixels.
- Fingerprint the device Derive a stable device/OS fingerprint from the un-noised output, defeating the Private Browsing protection.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
ScriptExecutionContext::advancedPrivacyProtections (override) / Document::advancedPrivacyProtections / noiseInjectionPolicySource/WebCore/dom/Document.cpp |
modified | Adds an override point returning the active advanced-privacy-protection flags; Document sources them from the top document's loader. |
Page::setupForRemoteWorkerSource/WebCore/page/Page.cpp |
modified | Lets shared/service workers pass in privacy protections when initializing the remote Page, so its contexts carry the flags. |
WorkerScriptLoader (advancedPrivacyProtections) / WorkerInitializationData / WorkerParametersSource/WebCore/workers/WorkerScriptLoader.cpp |
modified | Tracks the active privacy protections for the loading worker and plumbs them into WorkerInitializationData/WorkerParameters for shared/service workers. |
WorkerGlobalScope / WorkerOrWorkletGlobalScope constructorsSource/WebCore/workers/WorkerGlobalScope.cpp |
modified | Propagates the passed-in privacy-protection state into the worker global scope so OffscreenCanvas readback applies noise. |
Files Changed
Source/WebCore/Modules/webaudio/AudioWorkletMessagingProxy.cppSource/WebCore/dom/Document.cppSource/WebCore/dom/Document.hSource/WebCore/dom/EmptyScriptExecutionContext.hSource/WebCore/dom/ScriptExecutionContext.hSource/WebCore/page/Page.cppSource/WebCore/page/Page.hSource/WebCore/workers/Worker.cppSource/WebCore/workers/WorkerGlobalScope.cppSource/WebCore/workers/WorkerInitializationData.hSource/WebCore/workers/WorkerMessagingProxy.cppSource/WebCore/workers/WorkerOrWorkletGlobalScope.cppSource/WebCore/workers/WorkerOrWorkletGlobalScope.hSource/WebCore/workers/WorkerScriptLoader.cppSource/WebCore/workers/WorkerScriptLoader.hSource/WebCore/workers/WorkerThread.cppSource/WebCore/workers/WorkerThread.hSource/WebCore/workers/service/ServiceWorkerClientData.cppSource/WebCore/workers/service/ServiceWorkerClientData.hSource/WebCore/workers/service/context/ServiceWorkerThread.cppSource/WebCore/workers/service/context/ServiceWorkerThread.hSource/WebCore/workers/service/context/ServiceWorkerThreadProxy.cppSource/WebCore/workers/service/server/SWServer.cppSource/WebCore/workers/service/server/SWServer.hSource/WebCore/workers/service/server/SWServerToContextConnection.hSource/WebCore/workers/shared/SharedWorkerScriptLoader.cppSource/WebCore/workers/shared/context/SharedWorkerThreadProxy.cppSource/WebCore/worklets/WorkletGlobalScope.cppSource/WebCore/worklets/WorkletParameters.hSource/WebKit/NetworkProcess/ServiceWorker/WebSWServerConnection.cppSource/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.cppSource/WebKit/NetworkProcess/ServiceWorker/WebSWServerToContextConnection.hSource/WebKit/Shared/WebCoreArgumentCoders.serialization.inSource/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cppSource/WebKit/WebProcess/Storage/WebSWContextManagerConnection.hSource/WebKit/WebProcess/Storage/WebSWContextManagerConnection.messages.inSource/WebKit/WebProcess/Storage/WebSharedWorkerContextManagerConnection.cppTools/TestWebKitAPI/Tests/WebKit/AdvancedPrivacyProtections.mm
Audit Directions
- Privacy-flag plumbing across contextsAudit every ScriptExecutionContext subtype (Document, dedicated/shared/service workers, worklets) for consistent propagation of the noise salt and AdvancedPrivacyProtections used by canvas/OffscreenCanvas.
- Remote-Page state gapsGrep Page::setupForRemoteWorker and remote-worker init for other per-origin privacy/security state that a remote Page may be missing.
Patch
diff --git a/Source/WTF/wtf/PlatformEnable.h b/Source/WTF/wtf/PlatformEnable.h index 38d367cb578c..eef9de469a96 100644 --- a/Source/WTF/wtf/PlatformEnable.h +++ b/Source/WTF/wtf/PlatformEnable.h @@ -421,10 +421,6 @@ #define ENABLE_MOUSE_FORCE_EVENTS 1 #endif -#if !defined(ENABLE_NETSCAPE_PLUGIN_METADATA_CACHE) -#define ENABLE_NETSCAPE_PLUGIN_METADATA_CACHE 0 -#endif - #if !defined(ENABLE_NOTIFICATION_EVENT) #define ENABLE_NOTIFICATION_EVENT 0 #endif