Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in Chrome for iOS
DescriptionInsufficient validation of untrusted input in Chrome for iOS
ComponentChrome for iOS
Bug ClassLogic Error
Tracker516869032
Fix commit3c83a4e5c8ed (chromium/src) +7/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Files Changed

  • ios/web/webui/web_ui_messaging_java_script_feature.mm
From 3c83a4e5c8edb7b1f2ac8f230b3ad616174fcddd Mon Sep 17 00:00:00 2001
From: Mike Dougherty <michaeldo@chromium.org>
Date: Mon, 15 Jun 2026 10:38:38 -0700
Subject: [PATCH] Discard WebUI messages if the security_origin does not match the commited url.

Fixed: 516869032
Change-Id: I3b7e1493b1780b289b254acc6b7d53be7b8e54b6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7934275
Commit-Queue: Mike Dougherty <michaeldo@chromium.org>
Reviewed-by: Gauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1646921}
---

diff --git a/ios/web/webui/web_ui_messaging_java_script_feature.mm b/ios/web/webui/web_ui_messaging_java_script_feature.mm
index 778f20dc..68a65ac 100644
--- a/ios/web/webui/web_ui_messaging_java_script_feature.mm
+++ b/ios/web/webui/web_ui_messaging_java_script_feature.mm
@@ -49,6 +49,13 @@
     return;
   }
 
+  if (!script_message.security_origin().IsSameOriginWith(
+          url::Origin::Create(url.value()))) {
+    // Discard the message as the committed origin does not match the request
+    // URL
+    return;
+  }
+
   if (!script_message.body() || !script_message.body()->is_dict()) {
     return;
   }
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential WebUI message bypass on iOS via provisional navigation request URL check

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: The WebUI message dispatcher on iOS relies on the provisional request URL rather than the committed security origin when validating incoming messages. During a provisional WebUI navigation, a compromised WebContent process can exploit this mismatch to invoke privileged WebUI message handlers before navigation commits. This creates a potential sandbox escape path from the WebContent process to the browser process.

Affected files:

  • ios/web/webui/web_ui_messaging_java_script_feature.mm
  • ios/web/navigation/crw_wk_navigation_handler.mm
  • ios/web/web_state/web_state_impl_realized_web_state.mm

Estimated timestamp from git blame: 2023-01-17

Root Cause Analysis

In Chrome for iOS, WebUIMessagingJavaScriptFeature::ScriptMessageReceived acts as the browser-process handler for chrome.send() WebUI messages. This function authorizes dispatch by checking that the message is from the main frame and that its URL is an app-specific URL:

// ios/web/webui/web_ui_messaging_java_script_feature.mm
void WebUIMessagingJavaScriptFeature::ScriptMessageReceived(
    WebState* web_state, const ScriptMessage& script_message) {
  if (!script_message.is_main_frame()) return;
  std::optional<GURL> url = script_message.request_url();
  if (!url || !web::GetWebClient()->IsAppSpecificURL(url.value())) return;
  ...
  web_state_impl->HandleWebUIMessage(url.value(), *message_content, *arguments);
}

The field request_url() is populated in ios/web/js_messaging/java_script_content_world.mm from script_message.frameInfo.request.URL. This represents the provisional request URL of the frame during a navigation, which updates before the navigation actually commits.

However, the browser-side gate check in WebStateImpl::RealizedWebState::HandleWebUIMessage relies on HasWebUI():

void WebStateImpl::RealizedWebState::HandleWebUIMessage(...) {
  if (!HasWebUI()) return;
  web_ui_->ProcessWebUIIOSMessage(source_url, message, args);
}

Because the WebUIIOS controller is instantiated pre-commit during decidePolicyForNavigationAction in ios/web/navigation/crw_wk_navigation_handler.mm, HasWebUI() returns true before the navigation commits and while the previous document (e.g., https://evil.example) is still executing.

Since the validation checks request_url() (provisional) instead of the committed security origin of the frame (security_origin()), a compromised WebContent process can initiate a WebUI navigation and immediately post a forged message to invoke privileged WebUI handlers.

Potential Attack Scenario

Note: These are suggested/potential steps. Our tooling does not currently have the capability to execute code or run a live proof of concept to verify this behavior.

  1. A compromised WebContent process is active on a standard web page (e.g., https://evil.example).
  2. The compromised process triggers a back-forward navigation to an app-specific WebUI URL (e.g., chrome://flags).
  3. The browser process receives the provisional navigation request and instantiates the FlagsUI controller pre-commit, setting HasWebUI() to true.
  4. Before the navigation commits, the compromised document on https://evil.example posts a script message via DidPostMessage under the "WebUIMessage" handler.
  5. The browser process maps the message’s request_url to the provisional chrome://flags URL and validates it as app-specific.
  6. The browser process verifies HasWebUI() is true and dispatches the message to FlagsUI handlers in the unsandboxed browser process.

Suggested Fix

Modify the message validation path in WebUIMessagingJavaScriptFeature::ScriptMessageReceived to verify the sender’s origin against the web view’s committed URL, or use the verified security_origin() of the frame to make the security decision:

if (!script_message.security_origin().IsSameOriginWith(url::Origin::Create(url.value()))) {
  // Discard the message as the committed origin does not match the request URL
  return;
}

Evaluated with Chrome root at commit: b1520ef4a76878853a31f0943b565e42060edec8


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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