Medium firefox Cross Origin 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionSame-origin policy bypass in the Networking: Cookies component
ComponentDOM
Bug ClassCross Origin
Tracker2034944
Fix commitd0b8a022bca3 (firefox) +14/-2
CISA KEVNot listed
CreditedYaqoub Aldurayhim
Disclosed2026-06-16

Background

`CookieStore`
the DOM Cookie Store API object that lets a page asynchronously read cookies and receive change notifications for its origin.
`CookieStoreNotifier`
an nsIObserver that listens for global cookie-change notifications and dispatches matching change Events to a page’s CookieStore.
Base domain
the registrable domain (e.g. example.com) shared by every subdomain, which alone cannot distinguish a.example.com from b.example.com.
`CookieCommons::DomainMatches`
a networking helper that tests whether a given Cookie’s domain scope actually applies to a specific host.

Root Cause Analysis

CookieStoreNotifier::Create built a notifier keyed only on baseDomain and principal->OriginAttributesRef(), and CookieStoreNotifier::Observe forwarded any observed cookie change whose base domain and origin attributes matched, without checking the concrete host of the subscribing principal. The invariant that a page must only observe cookie changes actually scoped to its own origin was violated, because all subdomains of a registrable domain share the same base domain, so a change to a cookie scoped to a sibling host (or a host-only cookie on another subdomain) was delivered to an unrelated origin’s CookieStore.

The fix captures the principal’s GetAsciiHost into a new mHost member at creation time and, in Observe, calls net::CookieCommons::DomainMatches(net::Cookie::Cast(cookie), mHost) to drop any notification whose cookie does not actually apply to that host. This restores same-origin scoping because DomainMatches enforces the real host/domain rules that base-domain equality could not.

Key insight
The single mistake was scoping cookie-change notifications by shared base domain instead of by the subscriber’s actual host, letting sibling subdomains cross-observe each other’s cookie changes; the fix adds a per-host DomainMatches filter so only cookies that truly apply to the subscribing host are delivered.

Attack Path

  1. Establish sibling origins An attacker controls or influences one subdomain (e.g. evil.example.com) sharing a registrable base domain with a victim subdomain (app.example.com).
  2. Subscribe to cookie changes The attacker page opens a CookieStore and registers change listeners, creating a CookieStoreNotifier scoped only to the shared base domain and origin attributes.
  3. Trigger victim cookie writes Cookie changes occur on the victim host, either via normal victim activity or attacker-induced navigation/requests.
  4. Receive cross-origin notifications Because Observe matched on base domain without a host check, the attacker’s CookieStore receives change events for cookies scoped to the victim host, leaking their names and values.

Impact Assessment

An attacker page gains observation of cookie-change events (including names and values) belonging to a different host within the same registrable domain, a same-origin policy bypass in the content/DOM context of the web page. The precondition is that the attacker controls a page on a sibling subdomain of the target that shares the base domain and origin attributes; the leak is moderate because it exposes cross-origin cookie data rather than granting code execution.

Changed Functions

FunctionChangeNotes
mOriginAttributes
dom/cookiestore/CookieStoreNotifier.cpp
modified

Files Changed

  • dom/cookiestore/CookieStoreNotifier.cpp
  • dom/cookiestore/CookieStoreNotifier.h

Audit Directions

  • Base-domain scoping
    Flag any code that authorizes or routes data using baseDomain/registrable-domain equality alone, since it cannot separate sibling subdomains and needs an additional host or full-origin check.
  • Observer fan-out filters
    Audit nsIObserver::Observe implementations that dispatch security-sensitive events to per-origin objects, ensuring each notification is re-validated against the subscriber’s exact origin rather than a coarse key.
  • Cookie domain matching
    Wherever cookies are matched to a consumer, confirm CookieCommons::DomainMatches (or equivalent host-vs-domain logic) is applied, not just origin attributes or domain prefixes.
diff --git a/dom/cookiestore/CookieStoreNotifier.cpp b/dom/cookiestore/CookieStoreNotifier.cpp
index 73f2b3e9617..1a6561fea35 100644
--- a/dom/cookiestore/CookieStoreNotifier.cpp
+++ b/dom/cookiestore/CookieStoreNotifier.cpp
@@ -47,13 +47,18 @@ already_AddRefed<CookieStoreNotifier> CookieStoreNotifier::Create(
     return nullptr;
   }
 
+  nsCString host;
+  if (NS_WARN_IF(NS_FAILED(principal->GetAsciiHost(host))) || host.IsEmpty()) {
+    return nullptr;
+  }
+
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (NS_WARN_IF(!os)) {
     return nullptr;
   }
 
   RefPtr<CookieStoreNotifier> notifier = new CookieStoreNotifier(
-      aCookieStore, baseDomain, principal->OriginAttributesRef());
+      aCookieStore, baseDomain, host, principal->OriginAttributesRef());
 
   nsresult rv =
       os->AddObserver(notifier,
@@ -68,9 +73,10 @@ already_AddRefed<CookieStoreNotifier> CookieStoreNotifier::Create(
 
 CookieStoreNotifier::CookieStoreNotifier(
     CookieStore* aCookieStore, const nsACString& aBaseDomain,
-    const OriginAttributes& aOriginAttributes)
+    const nsACString& aHost, const OriginAttributes& aOriginAttributes)
     : mCookieStore(aCookieStore),
       mBaseDomain(aBaseDomain),
+      mHost(aHost),
       mOriginAttributes(aOriginAttributes) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCookieStore);
@@ -129,6 +135,10 @@ CookieStoreNotifier::Observe(nsISupports* aSubject, const char* aTopic,
     return NS_OK;
   }
 
+  if (!net::CookieCommons::DomainMatches(net::Cookie::Cast(cookie), mHost)) {
+    return NS_OK;
+  }
+
   bool isHttpOnly;
   rv = cookie->GetIsHttpOnly(&isHttpOnly);
   if (NS_WARN_IF(NS_FAILED(rv))) {
diff --git a/dom/cookiestore/CookieStoreNotifier.h b/dom/cookiestore/CookieStoreNotifier.h
index 7eefee65b35..b064293fc3e 100644
--- a/dom/cookiestore/CookieStoreNotifier.h
+++ b/dom/cookiestore/CookieStoreNotifier.h
@@ -30,6 +30,7 @@ class CookieStoreNotifier final : public nsIObserver {
 
  private:
   CookieStoreNotifier(CookieStore* aCookieStore, const nsACString& aBaseDomain,
+                      const nsACString& aHost,
                       const OriginAttributes& aOriginAttributes);
   ~CookieStoreNotifier();
 
@@ -39,6 +40,7 @@ class CookieStoreNotifier final : public nsIObserver {
   CookieStore* mCookieStore;
 
   nsCString mBaseDomain;
+  nsCString mHost;
   OriginAttributes mOriginAttributes;
 
   nsTArray<RefPtr<Event>> mDelayedDOMEvents;
Loading diff…