[Site Isolation] Authorize cookie IPC against the sites a web process hosts
Site isolation gave iframes their embedder as first party, and its cookies too.
Component: WebKit Site Isolation | a5cb75b
Source/WebKit/NetworkProcess/NetworkConnectionToWebProcess.cpp
Source/WebKit/UIProcess/WebProcessProxy.cpp
Under site isolation, cross-site iframes run in separate WebContent processes, and the network process is the sole holder of the cookie store. It authorized cookie IPC (cookiesForDOM, setCookiesFromDOM, GetRawCookies, SetRawCookie, DeleteCookie, domCookiesForHost and others) by checking the firstParty a renderer supplied — but site isolation deliberately grants an out-of-process iframe its embedder's domain as an allowed first party, and cookies are selected by url, which was never checked.
The UI process now sends each process's hosted-domain set (WebProcessProxy::hostedDomains) to the network process, and validateCookieAccess requires the request url's registrable domain to be in it. cookieRequestHeaderFieldValueDigest opts out via AllowUnhostedURL::Yes, and domCookiesForHost now replies std::optional so WebCookieCache doesn't cache a denial as an empty jar. The check stays permissive (std::nullopt) with site isolation off, before a process is used for site isolation, in single-process mode, for web archives, and for remote-worker processes.
Before:
iframe proc (evil.com) --IPC(firstParty=embedder, url=embedder)--> NetworkProcess
validate(firstParty) OK --> returns embedder's cookies (HttpOnly too)
After:
UIProcess: hostedDomains() --setHostedDomains--> NetworkProcess
iframe proc --IPC(firstParty=embedder, url=embedder)--> validate(firstParty) OK
hostsDomain(RegistrableDomain{url})?
nullopt set | empty domain | contains() --> allow
otherwise --> Disallow (domCookiesForHost: nullopt)
Significance
A compromised cross-site iframe process could previously name its embedder's URL and read or write that site's cookies, HttpOnly included, over IPC. This is a new security boundary on a high-value target, built as an allowlist with several deliberate fail-open paths.
Audit directions
- Fail-open allowlists.
m_hostedDomains == nulloptanddomain.isEmpty()both allow. Enumerate which URLs yield an emptyRegistrableDomain(file:, data:, blob:, about:, IP hosts, localhost, odd public-suffix cases) and whether a caller can chooseurlto land there. - Coverage. The check lives only in
validateCookieAccess; confirm every cookie-touching message, including the cookieStore async APIs, routes through it. The rejection branch has no API test, because only an out-of-process iframe can forge the read and the IPC testing API is injected only into main frames. - Async policy propagation.
sendHostedDomainsToNetworkProcessis asynchronous relative to commit and navigation; look for windows where a process already hosts a site but the network process's set is stale or still nullopt. - Set lifetime. Committed sites are retained after frames go away (unload handlers may use cookies),
HostedSiteTrackingonly moves forward, andm_sharedProcessDomainsis cleared indidStartUsingProcessForSiteIsolation. Check whether stale committed sites widen access for a later tenant of a reused process. - Exempt oracles.
cookieRequestHeaderFieldValueDigestaccepts an arbitrary cross-siteurl, and its salt is per network-process launch and shared across web processes; check whether repeated calls give a compromised renderer a change-oracle for another site's cookies. Also check otherDomCookiesForHostconsumers for nullopt mishandling.