← All issues

[5] [Navigation API] intercept() wrongly succeeds for cross-subdomain navigations

Severity: Medium | Component: WebCore Navigation API | b537a57

Rated Medium because the observable effect is a same-origin-policy weakening that lets any subdomain intercept top-level navigations to sibling subdomains, the analyst's confidence is 0.95, and the impact is bounded to web-platform trust violation (no memory-safety primitive).

Source/WebCore/page/Navigation.cpp

const URL& documentURL = document.url();
Ref documentOrigin = document.securityOrigin();
auto targetOrigin = SecurityOrigin::create(targetURL);
- bool isSameSite = documentOrigin->isSameSiteAs(targetOrigin);
- bool isSameOrigin = documentOrigin->isSameOriginAs(targetOrigin);
 
- // For cross-window navigation with document.domain, we need to check same-origin rather than same-site
- // to account for document.domain modifications that make cross-origin windows same-origin-domain
- if (!isSameSite && !isSameOrigin)
+ if (!documentOrigin->isSameOriginAs(targetOrigin))
+ return false;
+
+ if (documentURL.user() != targetURL.user() || documentURL.password() != targetURL.password())
return false;

documentCanHaveURLRewritten previously returned false only when both isSameSiteAs and isSameOriginAs were false — a strictly weaker predicate than the spec demands. The fix replaces the composite check with two separate tests: a strict isSameOriginAs (scheme/host/port equality) and an explicit URL::user()/URL::password() comparison. Two regression tests in NavigationAPI.mm exercise cross-subdomain navigation (page1.example.compage2.example.com) and userinfo-differing navigation, both verifying that event.intercept() now throws SecurityError.

Same-origin policy relaxation via substituting same-site for same-origin in a spec-mandated origin-equality check.

The Navigation API's NavigateEvent.canIntercept boolean gates whether event.intercept() is permitted; calling intercept() when canIntercept is false throws SecurityError. Per the HTML spec, canIntercept is true only if the document's URL "can be rewritten" to the destination URL, which requires equality of scheme, username, password, host, and port. SecurityOrigin::isSameOriginAs compares (scheme, host, port); SecurityOrigin::isSameSiteAs compares the registrable domain (eTLD+1) and is therefore true for any pair of subdomains under a shared public suffix + 1 label.

The pre-fix implementation took the form if (!isSameSite && !isSameOrigin) return false; — a De Morgan-inverted composition that means "pass if EITHER same-site OR same-origin". The stale inline comment about document.domain reveals the author's intent (prefer isSameOriginAs to account for document.domain aliasing) but the boolean shape over-relaxed to the entire eTLD+1.

🔒

An origin-comparison primitive in a high-level Web API was implemented with the wrong predicate — the trust-boundary implications and concrete attack scenarios are explored.

Subscribe to read more

🔒

Several reusable audit patterns identified for finding the same class of mistake elsewhere in WebKit's web-platform security predicates.

Subscribe to read more