← WebKit — 2026-W36

bc550432a2f0e7909c257292ac848af57eb9a485  SharedWorker process placement potential spoofing

severity high class CrossOrigin confidence 0.8 signal score 8.07 Source/WebKit/NetworkProcess patch-now AI review CONFIRMED
Primitive: compromised WebContent process forges only the scheme of a shared worker's top origin (scheme-blind first-party check) to launch a worker for a site it does not own
Triage note: Extends the NetworkProcess allowed-first-parties map from RegistrableDomain to full WebCore::Site and adds allowsFirstPartySite (scheme + domain). WebSharedWorkerServerConnection::requestSharedWorker now MESSAGE_CHECKs the full top Site instead of just the registrable domain (rdar://183347241), closing a scheme-forgery path that could spin up a shared worker committed to an unowned site.
Structural signals: smartptr_member_added (+4.87), uaf_lifetime (+2.2), msg_soft (+1.0)

The bug at a glance

The NetworkProcess per-process allow-list of first parties stored only registrable domains (scheme-blind), and WebSharedWorkerServerConnection::requestSharedWorker validated a shared worker’s claimed top origin by registrable domain alone. A compromised WebContent process could forge only the scheme of the top origin (e.g. claim https://victim when it owns a different-scheme origin of the same host) and have the UIProcess launch a shared worker process committed to a site it does not own. This is a cross-origin / site-isolation boundary breach in the NetworkProcess, not memory corruption; the proven impact is running a SharedWorker under an unowned origin, which gives that origin’s cookie/storage/cache context. High severity is justified as a cross-origin confused-deputy; RCE is not claimed.

A scheme-stripping confused-deputy: the check that was supposed to pin a worker to its owning site compared only the registrable domain, so http vs https (or a custom scheme) collapsed to the same key and the enforcement silently permitted a forged scheme.

Root cause

The invariant is that a WebContent process may only request a SharedWorker for a top origin (scheme + registrable domain) it is actually permitted to represent. The allow-list m_allowedFirstPartiesForCookies was a HashMap<ProcessIdentifier, pair<LoadedWebArchive, HashSet» — RegistrableDomain discards the scheme.

Pre-patch exploit sequence: 1. a compromised WebContent process issues requestSharedWorker with a SharedWorkerKey whose origin.topOrigin names a scheme it does not own but a host it (or any peer) does; 2. the handler builds RegistrableDomain::uncheckedCreateFromHost(sharedWorkerKey.origin.topOrigin.host()) — scheme discarded; 3. MESSAGE_CHECK passes because allowsFirstPartyForCookies matches on registrable domain only; 4. WebSharedWorker::topSite() and WebProcessPool::establishRemoteWorkerContextConnectionToNetworkProcess use the forged top site to choose/launch the process that runs the worker, committing a process to a site no process legitimately owns.

The fix widens the allow-list to carry full WebCore::Site (scheme + registrable domain): the per-process value becomes struct AllowedFirstParties { LoadedWebArchive; HashSet domains; HashSet sites; }. A new allowsFirstPartySite() compares the whole Site. requestSharedWorker now constructs WebCore::Site topSite { sharedWorkerKey.origin.topOrigin } and, when topSite.protocol() is non-empty, MESSAGE_CHECKs allowsFirstPartySite(m_webProcessIdentifier, topSite). The IPC message AddAllowedFirstPartyForCookies and addAllowedFirstPartyForCookies() are retyped from RegistrableDomain to Site; a sibling addAllowedFirstPartyDomainForCookies() keeps the old domain-only path for the cookie allow-list, and callers that only have a host (content-filter block pages, NetworkSession, launchSharedWorker) are redirected to the domain-only variant. Opaque top origins are deliberately skipped because every opaque origin collapses to the same Site sentinel (invalidProtocol), and legitimate CSP-sandboxed documents with opaque origins may name data: worker scripts.

Key code

Shared-worker top origin now validated as a full Site (scheme+domain)

WebCore::Site topSite { sharedWorkerKey.origin.topOrigin };
if (!topSite.protocol().isEmpty())
    MESSAGE_CHECK(m_networkProcess->allowsFirstPartySite(m_webProcessIdentifier, topSite));
// was: MESSAGE_CHECK(allowsFirstPartyForCookies(..., RegistrableDomain::uncheckedCreateFromHost(
//          sharedWorkerKey.origin.topOrigin.host())) != AllowCookieAccess::Terminate);

Fix approach

The patch promotes the per-process first-party allow-list from a registrable-domain set to a Site set that records scheme, and adds allowsFirstPartySite() which (unlike allowsFirstPartyForCookies) compares scheme as well as domain. requestSharedWorker validates the full claimed top Site, closing the scheme-forgery gap. To avoid over-tightening the cookie path (which legitimately keys on registrable domain), the change keeps a parallel addAllowedFirstPartyDomainForCookies() and routes host-only callers there. It deliberately exempts opaque top origins (protocol().isEmpty() sentinel) so that CSP-sandboxed documents, which legitimately have opaque origins and may host data: workers, are not broken. Net effect: the process-to-site commitment for shared workers now requires the exact scheme the process was granted.

Patch walkthrough

  • Source/WebKit/NetworkProcess/SharedWorker/WebSharedWorkerServerConnection.cpp — requestSharedWorker now builds a WebCore::Site from the claimed top origin and MESSAGE_CHECKs allowsFirstPartySite (skipping opaque origins), instead of the scheme-blind registrable-domain check.
  • Source/WebKit/NetworkProcess/NetworkProcess.cpp — Replaces the pair<LoadedWebArchive,HashSet> value with an AllowedFirstParties struct holding both domains and sites; adds allowsFirstPartySite(), retypes addAllowedFirstPartyForCookies() to take a Site and populate sites, and splits out addAllowedFirstPartyDomainForCookies() for domain-only callers.
  • Source/WebKit/NetworkProcess/NetworkProcess.h — Declares the AllowedFirstParties struct and allowsFirstPartySite()/addAllowedFirstPartyDomainForCookies(), and includes WebCore/Site.h.
  • Source/WebKit/NetworkProcess/NetworkProcess.messages.in — AddAllowedFirstPartyForCookies IPC parameter changes from WebCore::RegistrableDomain to WebCore::Site so the scheme is carried across the boundary.
  • Source/WebKit/Shared/NetworkProcessConnectionParameters.h — allowedFirstPartiesForCookies in the connection parameters becomes HashSetWebCore::Site so initial seeding of the allow-list is scheme-aware.
  • Source/WebKit/NetworkProcess/NetworkResourceLoader.cpp / NetworkSession.cpp / SharedWorker/WebSharedWorkerServerToContextConnection.cpp — Host-only/legacy callers are redirected to addAllowedFirstPartyDomainForCookies() to preserve the registrable-domain cookie behavior.

Background

SharedWorker and its top origin — A SharedWorker is scoped to an origin; its claimed top origin decides which (possibly new) process the UIProcess spins up to execute it, so forging that origin commits a process to an unowned site.

WebCore::Site vs RegistrableDomain — RegistrableDomain is the eTLD+1 host portion only; WebCore::Site pairs the registrable domain with the scheme (protocol), so comparing Sites distinguishes http://x from https://x.

First-party allow-list (m_allowedFirstPartiesForCookies) — A NetworkProcess per-process map seeded by the trusted UIProcess recording which first parties a WebContent process may represent; used to reject processes supplying origins they do not own.

Opaque origin / invalidProtocol sentinel — An opaque origin (e.g. CSP-sandboxed or data:) has no meaningful scheme and all opaque origins collapse to the same Site, so a Site-equality check cannot distinguish them and is skipped.

Remote worker context connection — establishRemoteWorkerContextConnectionToNetworkProcess is the UIProcess/NetworkProcess path that launches the process hosting the worker based on topSite(); it is the sink that trusts the validated origin.

Vulnerability window

  1. Setup — Attacker controls a WebContent process and knows a victim host it does not own in the requested scheme.
  2. Trigger — It sends requestSharedWorker with a SharedWorkerKey whose topOrigin forges the scheme (same host, wrong/unowned scheme).
  3. Pre-patch — The registrable-domain-only MESSAGE_CHECK passes; the UIProcess launches a worker process committed to the forged site.
  4. Post-patch — allowsFirstPartySite compares the scheme and the forged Site is absent from the process’s sites set, so MESSAGE_CHECK fails and the request is rejected.

Proof of concept

No regression test is included. Conceptual trigger (inferred): from a compromised WebContent process, craft the requestSharedWorker IPC with origin.topOrigin.scheme set to a scheme the process was not granted but whose host it can plausibly reference; pre-patch the worker launches under the forged site. There is no pure-JS path because the scheme of the top origin is not script-forgeable in an uncompromised renderer; the attack is at the IPC layer (inferred).

Exploitation

  1. Reach — Compromised WebContent process issuing NetworkProcess SharedWorker IPC.
  2. Trigger — Forge only the scheme of the SharedWorker top origin so the registrable domain still matches an allowed entry.
  3. Primitive — Launch a SharedWorker (and possibly a fresh process) committed to a site the process does not own, obtaining that scheme-origin’s execution context.
  4. Escalation (inferred) — Via the unowned origin’s SharedWorker, reach that origin’s cookies/storage/cache and cross-origin messaging; whether this yields broader data theft depends on what the targeted scheme-origin exposes to a SharedWorker.

Detection & hunting

For defenders and detection engineers:

  • allowsFirstPartySite MESSAGE_CHECK failures —
  • Scheme/host split in worker launches —
  • Allow-list seeding mismatches —

Remaining risk in the patch

  • Opaque-origin exemption — The protocol().isEmpty() skip intentionally bypasses the Site check for opaque top origins; if a non-opaque origin could ever be made to report an empty protocol, or if an opaque origin can still influence process selection, that path is unguarded by construction.
  • Cookie path still domain-only — allowsFirstPartyForCookies and addAllowedFirstPartyDomainForCookies remain scheme-blind; only the shared-worker commitment was tightened, so any other consumer that needs scheme fidelity but calls the domain API is still vulnerable.
  • topSite()/establishRemoteWorkerContextConnection not in diff — The sink that selects/launches the process from topSite() is not shown, so the assumption that the validated Site fully determines the launched process’s site is unverified here (inferred).

Audit directions

  • Other registrable-domain-only origin checks —
  • Site vs RegistrableDomain allow-list consumers —
  • Opaque-origin handling —

Before / after

Loading diff…