Medium chrome Cross Origin 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in CORS
DescriptionInsufficient validation of untrusted input in CORS
ComponentCORS
Bug ClassCross Origin
Tracker497432281
Fix commit583163b3b6df (chromium/src) +0/-12
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Files Changed

  • services/network/cors/cors_url_loader.cc
  • services/network/public/cpp/features.cc
  • services/network/public/cpp/features.h
From 583163b3b6df95304e03dcb3a7d97f191d38072b Mon Sep 17 00:00:00 2001
From: Takashi Toyoshima <toyoshim@chromium.org>
Date: Fri, 07 Aug 2026 06:27:25 -0700
Subject: [PATCH] Remove network::features::kIgnoreCorsPreflightPolicy

Remove the expired feature flag kIgnoreCorsPreflightPolicy and its usage
in CorsURLLoader::NeedsPreflight.

Change-Id: Ia05b681fe92123d16022b20c34820037bbf72654
Bug: 497432281
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8214839
Reviewed-by: Adam Rice <ricea@chromium.org>
Commit-Queue: Takashi Toyoshima <toyoshim@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1675666}
---

diff --git a/services/network/cors/cors_url_loader.cc b/services/network/cors/cors_url_loader.cc
index 1ff2b5d..7d31e958 100644
--- a/services/network/cors/cors_url_loader.cc
+++ b/services/network/cors/cors_url_loader.cc
@@ -94,12 +94,6 @@
     return PreflightRequiredReason::kCorsWithForcedPreflightMode;
   }
 
-  if (!base::FeatureList::IsEnabled(features::kIgnoreCorsPreflightPolicy) &&
-      request.cors_preflight_policy ==
-          mojom::CorsPreflightPolicy::kPreventPreflight) {
-    return std::nullopt;
-  }
-
   if (!IsCorsSafelistedMethod(request.method))
     return PreflightRequiredReason::kDisallowedMethod;
 
diff --git a/services/network/public/cpp/features.cc b/services/network/public/cpp/features.cc
index b6c7b01..b65d81c 100644
--- a/services/network/public/cpp/features.cc
+++ b/services/network/public/cpp/features.cc
@@ -197,9 +197,6 @@
 // and continue the handshake without sending one if requested.
 BASE_FEATURE(kOmitCorsClientCert, base::FEATURE_DISABLED_BY_DEFAULT);
 
-// Ignore CorsPreflightPolicy and always perform CORS checks.
-BASE_FEATURE(kIgnoreCorsPreflightPolicy, base::FEATURE_ENABLED_BY_DEFAULT);
-
 // Enforces that frame-type destinations require kNavigate mode.
 BASE_FEATURE(kRestrictFrameDestinationsToNavigate,
              base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/services/network/public/cpp/features.h b/services/network/public/cpp/features.h
index f1698cc3..432ae312 100644
--- a/services/network/public/cpp/features.h
+++ b/services/network/public/cpp/features.h
@@ -81,9 +81,6 @@
 BASE_DECLARE_FEATURE(kOmitCorsClientCert);
 
 COMPONENT_EXPORT(NETWORK_CPP_FLAGS_AND_SWITCHES)
-BASE_DECLARE_FEATURE(kIgnoreCorsPreflightPolicy);
-
-COMPONENT_EXPORT(NETWORK_CPP_FLAGS_AND_SWITCHES)
 BASE_DECLARE_FEATURE(kRestrictFrameDestinationsToNavigate);
 
 COMPONENT_EXPORT(NETWORK_CPP_FLAGS_AND_SWITCHES)
Loading diff…

Original Bug Report

reported by vm...@google.com

CORS preflight bypass via renderer-controlled cors_preflight_policy

Project Fortify, an experimental security project, has identified the following potential security issue.

Overview: A compromised renderer can bypass Out-of-Renderer CORS (OOR-CORS) preflights by manipulating the cors_preflight_policy Mojo field. This allows attackers to send credentialed cross-origin requests with non-simple methods (e.g., DELETE) or headers without the mandatory OPTIONS preflight. This exposes cross-origin servers to state-changing CSRF attacks.

Affected files:

  • services/network/cors/cors_url_loader.cc
  • services/network/cors/cors_url_loader_factory.cc
  • services/network/public/mojom/url_request.mojom

Estimated timestamp from git blame: 2026-02-01

Description

There is a potential Out-of-Renderer CORS (OOR-CORS) bypass caused by missing validation of the cors_preflight_policy field when receiving a ResourceRequest from an untrusted renderer process.

The network::mojom::URLRequest Mojo struct includes a cors_preflight_policy field. When an untrusted renderer sends a request via URLLoaderFactory::CreateLoaderAndStart, the Network Service evaluates the request using CorsURLLoaderFactory::IsValidRequest() (in services/network/cors/cors_url_loader_factory.cc). While this function performs extensive validation on fields like load_flags and request_initiator, it completely ignores cors_preflight_policy.

Because this field is not sanitized, a compromised renderer can set it to mojom::CorsPreflightPolicy::kPreventPreflight. Later, when CorsURLLoader::NeedsPreflight() (in services/network/cors/cors_url_loader.cc) is called to determine if an OPTIONS preflight is necessary, it explicitly checks this field:

std::optional<PreflightRequiredReason> NeedsPreflight(
    const ResourceRequest& request) {
  // ...
  if (request.cors_preflight_policy ==
      mojom::CorsPreflightPolicy::kPreventPreflight) {
    return std::nullopt;
  }
  // ... checks for IsCorsSafelistedMethod and CorsUnsafeNotForbiddenRequestHeaderNames
}

Returning std::nullopt forces the Network Service to skip the preflight check entirely. This allows an attacker to send credentialed cross-origin requests with non-safelisted methods (e.g., DELETE, PUT) or custom headers without triggering the CORS preflight, leading to CSRF vulnerabilities against APIs that rely on CORS preflight enforcement.

Potential Steps to Trigger

Note: Our tooling agent cannot run code, so these are suggested theoretical steps to exploit the vulnerability.

  1. Compromise Renderer: An attacker compromises a renderer process, gaining the ability to craft raw Mojo messages.
  2. Craft Request: The attacker uses their Mojo access to construct a network::mojom::URLRequest targeting a cross-origin state-changing endpoint (e.g., https://target.com/api/delete-account).
  3. Set Non-Simple Attributes: The attacker sets the HTTP method to DELETE and sets credentials_mode to kInclude to send the victim’s ambient cookies.
  4. Exploit the Policy Field: The attacker sets the cors_preflight_policy field to kPreventPreflight (enum value 1).
  5. Send the Request: The attacker sends the IPC via the URLLoaderFactory::CreateLoaderAndStart endpoint provided to the renderer.
  6. Bypass Preflight: The Network Service (CorsURLLoaderFactory) accepts the request because IsValidRequest misses this field. CorsURLLoader sees kPreventPreflight, skips the OPTIONS request, and directly sends the DELETE request with cookies to the target server.
  7. CSRF Execution: The target server receives the DELETE request and processes it, assuming it was authorized by a successful preflight, resulting in a successful Cross-Site Request Forgery.

Suggested Fix

In CorsURLLoaderFactory::IsValidRequest() (services/network/cors/cors_url_loader_factory.cc), add a check to restrict cors_preflight_policy for untrusted callers. For example:

  if (!is_trusted_) {
    if (request.cors_preflight_policy != mojom::CorsPreflightPolicy::kConsiderPreflight) {
      mojo::ReportBadMessage(
          "CorsURLLoaderFactory: Untrusted caller attempting to bypass CORS preflight");
      return false;
    }
    // ... existing checks ...
  }

Alternatively, force the field to kConsiderPreflight for all requests originating from untrusted renderers.

Evaluated with Chrome root at commit: a9cbf6e8b275fe4147435aa905f3b7f5a656f5f0


Results from so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker