Medium CVSS 5.3 webkit Cross Origin 🔧 Commit mapped

Overview

Medium
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionCookies belonging to one origin may be sent to another origin
ComponentWebKit
Sub-componentWebKit UIProcess
Bug ClassCross Origin
Tracker279226
Fix commit11494e677291 (WebKit/WebKit)
CWECWE-346 (Origin validation error)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N
CISA KEVNot listed
CreditedWojciech Regula of SecuRing (
Disclosed2024-10-28

Background

Web archive (.webarchive)
A single-file snapshot of a page and its subresources that WebKit can load, with its own baseURL origin.
Ephemeral / non-persistent data store
A website data store with no on-disk persistence, swapped in for web archive loads to isolate their cookies and storage.
URL string vs filesystem path
url.string() is the full serialized URL (scheme, path, query, fragment, encoding); url.fileSystemPath() is the decoded local path only.
Navigation policy decision
The UIProcess step that decides how a navigation proceeds, including which data store backs it.

Root Cause Analysis

WebPageProxy::receivedNavigationActionPolicyDecision decides whether a navigation should be loaded in a swapped-in non-persistent (ephemeral) website data store, which is done for web archives so their content is isolated. The webarchive detection tested webarchiveURL.string().endsWith(".webarchive") — i.e. it matched the suffix against the full URL string, which includes the query and fragment and percent-encoding, not just the file path. A file URL for a genuine .webarchive that carries a query or fragment (e.g. file:///path/x.webarchive?a or ...x.webarchive#a) has a .string() that does NOT end in .webarchive, so the archive was not recognized and the ephemeral-store swap was skipped; the archive then loaded in the normal persistent data store, so cookies associated with the archive’s origin were read from / written to the shared persistent store and could reach another origin.

The fix matches against webarchiveURL.fileSystemPath(), the decoded filesystem path with query/fragment stripped, so the .webarchive classification reflects the real file rather than an attacker-manipulable URL string, and the ephemeral-store isolation is applied to exactly the webarchive loads it is meant for.

The restored invariant is that ‘is this a .webarchive load’ is decided from the file path, not from a URL string that can be altered without changing the file. This runs in the UIProcess navigation policy path, not the sandboxed WebContent.

Key insight
Classifying a web-archive load by the raw URL string (which includes query/fragment/encoding) instead of the filesystem path let an attacker hide or fake the .webarchive suffix, bypassing the ephemeral-store isolation and leaking cookies across origins.

Attack Path

  1. Host a web archive Get the victim to open a .webarchive file (local file load or substitute data) that should be isolated in an ephemeral data store.
  2. Perturb the URL string Reference the archive with a URL whose string does not end in ‘.webarchive’ though the file path does — e.g. append a query or fragment (x.webarchive?a / x.webarchive#a).
  3. Defeat archive detection endsWith on url.string() fails to match, so receivedNavigationActionPolicyDecision does not swap in the non-persistent store.
  4. Leak cookies across origins The archive loads in the persistent store, so cookies tied to the archive’s baseURL origin are read/written in the shared store and can be sent to another origin.

Impact Assessment

No memory-safety primitive; this is a privacy/isolation logic bug. Mis-detecting web archives caused the ephemeral-store isolation to be skipped, so cookies belonging to one origin could be sent to another origin. It affects data confidentiality/integrity of cookies and executes in the UIProcess navigation path; there is no path to code execution. Rated medium.

Files Changed

  • Source/WebKit/UIProcess/WebPageProxy.cpp

Audit Directions

  • Other suffix/extension checks on url.string()
    grep WebKit/WebCore for .string().endsWith( / .startsWith( / contains( on URLs used for security decisions; prefer fileSystemPath()/path() where a file is meant.
  • Data-store / storage-partition selection
    Audit other branches choosing persistent vs ephemeral stores or storage partitions based on URL shape rather than resolved origin.
  • webarchive handling
    Review all isWebArchiveMIMEType / .webarchive handling for consistent path-based classification across load, save, and policy paths.
diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp
index a36d0ca506c9..fd0ead8b1a10 100644
--- a/Source/WebKit/UIProcess/WebPageProxy.cpp
+++ b/Source/WebKit/UIProcess/WebPageProxy.cpp
@@ -4674,7 +4674,7 @@ void WebPageProxy::receivedNavigationActionPolicyDecision(WebProcessProxy& proce
     if (preferences().loadWebArchiveWithEphemeralStorageEnabled() && navigation && policyAction == PolicyAction::Use && navigationAction->navigationType() != NavigationType::Reload && !isPolicyDataStore) {
         bool isSubstituteWebArchive = navigation->substituteData() && MIMETypeRegistry::isWebArchiveMIMEType(navigation->substituteData()->MIMEType);
         auto webarchiveURL = isSubstituteWebArchive ? URL { navigation->substituteData()->baseURL } : navigation->currentRequest().url();
-        if (isSubstituteWebArchive || (webarchiveURL.protocolIsFile() && webarchiveURL.string().endsWith(".webarchive"_s))) {
+        if (isSubstituteWebArchive || (webarchiveURL.protocolIsFile() && webarchiveURL.fileSystemPath().endsWith(".webarchive"_s))) {
             WEBPAGEPROXY_RELEASE_LOG(Loading, "receivedNavigationActionPolicyDecision: Swapping in non-persistent websiteDataStore for web archive.");
             if (navigation->targetItem() && navigation->targetItem()->dataStoreForWebArchive())
                 websiteDataStore = *navigation->targetItem()->dataStoreForWebArchive();
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker.