[4] FrameLoader inherits empty registrable domain for about:blank popups
Rated Medium because the diff fixes a reliable third-party-cookie-blocking bypass reachable from any first-party page via window.open('about:blank'); impact is privacy/tracking, not memory safety.
When an about:blank popup made cross-origin requests, its firstPartyForCookies was set to about:blank, which has an empty registrable domain. thirdPartyCookieBlockingDecisionForRequest() returns ThirdPartyCookieBlockingDecision::None for empty domains, bypassing third-party cookie blocking. FrameLoader::updateFirstPartyForCookies() should inherit the opener's firstPartyForCookies when the main frame URL is an owner-inherited document.
Source/WebCore/loader/FrameLoader.cpp
LayoutTests/http/tests/resourceLoadStatistics/third-party-cookie-blocking-about-blank-popup.html
Patch Details
updateFirstPartyForCookies() previously set the frame's firstPartyForCookies to page->mainFrameURL() unconditionally. The fix adds a check: if the main frame URL is one that should inherit its security origin from its owner (via SecurityPolicy::shouldInheritSecurityOriginFromOwner()), it walks to page->mainFrame().opener() (downcast to LocalFrame), retrieves the opener document's firstPartyForCookies(), and uses that instead.
Failure to mirror opener/parent inheritance into the first-party-for-cookies field when the document URL is an owner-inherited scheme like about:blank.
Background
firstPartyForCookies is a per-frame URL recorded on outgoing requests; the network layer uses its registrable domain (eTLD+1) as the "first party" when deciding whether a cookie destination is first- or third-party. For about:blank and other opaque schemes, the registrable domain is the empty string. WebKit's third-party cookie blocking (Intelligent Tracking Prevention) suppresses cookies on cross-site subresource requests when the destination has been classified as a tracker; the decision is gated on thirdPartyCookieBlockingDecisionForRequest(). Per HTML spec, an about:blank document inherits its security origin from the browsing context that created it; SecurityPolicy::shouldInheritSecurityOriginFromOwner(url) returns true for the set of URL schemes (about:blank, about:srcdoc, data: etc.) that follow this rule.
Analysis
When a page opened an about:blank popup, the popup's main frame URL was literally about:blank. updateFirstPartyForCookies() then set firstPartyForCookies on the popup's frame to that URL. When the popup subsequently issued a cross-origin request, the network layer computed the registrable domain of about:blank — empty. thirdPartyCookieBlockingDecisionForRequest() treats an empty registrable domain as None, i.e. "do not block", because there is no meaningful first party to compare against.
From a first-party page A, call window.open('about:blank') to obtain a popup whose main frame URL is about:blank. Inside the popup, issue credentialed cross-origin requests, e.g. fetch('https://tracker.example/...', {credentials: 'include'}). Pre-fix, the popup's firstPartyForCookies is about:blank, its registrable domain is empty, and any previously-set third-party cookies for tracker.example are attached to the request — exactly the case the regression test demonstrates with cookieName = 'aboutBlankTestCookie'.
The fix only handles the main-frame opener chain via LocalFrame; if the opener is a RemoteFrame (site-isolation case), the dynamicDowncast<LocalFrame> fails silently and the original (empty-registrable-domain) URL is still used. Worth flagging for follow-up.
This vulnerability weakened the third-party cookie blocking trust boundary that ITP and resource-load-statistics rely on. The security model assumes that every top-level browsing context has a stable, non-empty first-party registrable domain; before the fix this invariant was violated for any popup whose initial document URL was owner-inherited. First-party-for-cookies is a parallel state to security origin, and any time a URL scheme has special origin-inheritance rules (about:blank, about:srcdoc, data:, sandboxed iframes, javascript: URLs) the cookie-policy first party must mirror the same inheritance — otherwise the network layer silently downgrades to "no first party".
Audit directions
- Privacy/security policy decisions keyed off a URL's registrable domain or string form, applied to documents whose URL is owner-inherited. Audit any caller of
registrableDomain(),RegistrableDomainconstruction, or string comparisons onmainFrameURL(). Grep formainFrameURL()andfirstPartyForCookiesacrossSource/WebCoreandSource/WebKit, and check each site for whether it accounts forSecurityPolicy::shouldInheritSecurityOriginFromOwner. - Incomplete opener/parent inheritance under site isolation. The fix downcasts
page->mainFrame().opener()toLocalFrameand bails silently if the opener is aRemoteFrame. Audit whether a cross-process opener still hits the empty-registrable-domain path, and whether the equivalent inheritance is performed in the UI/Network process for that case. ExamineWebPageProxy,WebFrameProxy::opener, and any IPC carryingfirstPartyForCookies. - Divergence between SecurityOrigin inheritance and FirstPartyForCookies inheritance. Audit other document creation paths —
srcdociframes,data:URL navigations,javascript:URL loads, sandboxed iframes withallow-same-origin,document.open()re-init — to verify each one updatesfirstPartyForCookiesconsistently. Start withDocument::initSecurityContext,FrameLoader::didBeginDocument, and all callers ofsetFirstPartyForCookies. - "Empty registrable domain == no policy applies" fail-open. Audit
thirdPartyCookieBlockingDecisionForRequestand sibling decision functions (storage access, partitioned cookies, third-party storage) to confirm that an empty first-party registrable domain is treated conservatively rather than asNone.