CVE-2026-28958
Overview
Background
- SameSite cookies
- A cookie attribute (Strict/Lax/None) controlling whether a cookie is sent on cross-site requests; a core CSRF/tracking defense.
- Same-site vs same-origin
- Same-site compares registrable domains (site); same-origin compares scheme+host+port. SameSite eligibility is a site-level check that depends on the request initiator.
- Request initiator / requester
- The document that initiated a load; its site is what a request should be classified same-site against.
- shouldInheritSecurityOriginFromOwner
- True for about:srcdoc/about:blank documents that inherit their owner’s origin; such initiators are handled conservatively (nullptr) here.
Root Cause Analysis
FrameLoader::load computed and attached SameSite cookie information to the outgoing request by calling addSameSiteInfoToRequestIfNeeded(loader->request()) WITHOUT passing the request’s initiator. As the commit title states (‘Initiator-omitted samesite classification can lead to SameSite=Strict cookie cross-site leakage’), omitting the initiator means the same-site determination is not made against the document that actually initiated the load, so a cross-site request could be misclassified as same-site and therefore have the target origin’s SameSite=Strict/Lax cookies attached — leaking those cookies to a cross-site context.
The fix takes Ref initiator = request.requester() and passes it to addSameSiteInfoToRequestIfNeeded, but passes nullptr when the initiator inherits its security origin from its owner (about:srcdoc/about:blank, via SecurityPolicy::shouldInheritSecurityOriginFromOwner) so such frames are treated conservatively instead of as a concrete same-site initiator.
The restored invariant is that SameSite eligibility is computed against the true initiating document’s site. INFERENCE: the addSameSiteInfoToRequestIfNeeded overload that consumes the initiator and the exact same-site comparison are outside this hunk; the diff establishes that the initiator was omitted and is now supplied.
Attack Path
- Attacker initiates a cross-site load An attacker-controlled document triggers a navigation/subresource load targeting a victim origin for which the user holds SameSite=Strict/Lax cookies.
- Same-site classification omits the initiator Pre-patch, addSameSiteInfoToRequestIfNeeded is called without the initiator, so the request’s same-site status is computed on a default/omitted basis.
- Cross-site request misclassified as same-site The classification treats the request as same-site, making SameSite-restricted cookies eligible.
- Leak SameSite cookies cross-site The victim origin’s SameSite=Strict/Lax cookies are attached to the cross-site request and delivered to the attacker-influenced endpoint, defeating the CSRF/cookie isolation the SameSite attribute provides.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
FrameLoader::loadSource/WebCore/loader/FrameLoader.cpp |
modified | Computes Ref initiator = request.requester() and passes it to addSameSiteInfoToRequestIfNeeded (nullptr when the initiator inherits its origin from its owner), so SameSite classification uses the true initiating document instead of omitting it. |
Files Changed
Source/WebCore/loader/FrameLoader.cppTools/TestWebKitAPI/Tests/WebKit/WKWebView/WKHTTPCookieStore.mm
Audit Directions
- Other addSameSiteInfoToRequestIfNeeded callersGrep every call to addSameSiteInfoToRequestIfNeeded and confirm each passes a concrete initiator; any call that omits it (single-argument form) is a candidate for the same misclassification.
- First-party / same-site computed without the initiatorLook for isSameSite / firstPartyForCookies / siteForCookies decisions in the loader and cookie layers that default the initiator or use the target URL alone; the tell is a same-site check lacking the requesting document.
- Origin-inheriting frames in cookie logicAudit how about:srcdoc/about:blank (shouldInheritSecurityOriginFromOwner) initiators are treated across the cookie/same-site paths; ensure they are handled conservatively rather than as a concrete same-site party (cross-reference CVE-2026-64728).
Patch
diff --git a/Source/WebCore/loader/MixedContentChecker.cpp b/Source/WebCore/loader/MixedContentChecker.cpp
index bd42f3d7c2bd..f7c90310966e 100644
--- a/Source/WebCore/loader/MixedContentChecker.cpp
+++ b/Source/WebCore/loader/MixedContentChecker.cpp
@@ -131,10 +131,6 @@ bool MixedContentChecker::canModifyRequest(const URL& url, FetchOptions::Destina
bool MixedContentChecker::shouldBlockRequest(Frame& frame, const URL& url, IsUpgradable isUpgradable)
{
- RefPtr<Document> document;
- if (auto* localFrame = dynamicDowncast<LocalFrame>(frame))
- document = localFrame->document();
-
#if ENABLE(CONTENT_FILTERING) && HAVE(WEBCONTENTRESTRICTIONS)
if (url == ContentFilter::blockedPageURL())
return false;