CVE-2026-87599
Overview
Files Changed
components/security_interstitials/core/insecure_form_util.cccomponents/security_interstitials/core/insecure_form_util_unittest.cc
Patch
From d7a95acf2a5af92b6d94bff01a6897c01df4dbb1 Mon Sep 17 00:00:00 2001
From: Ari Chivukula <arichiv@chromium.org>
Date: Tue, 28 Jul 2026 18:08:17 -0700
Subject: [PATCH] Resolve InsecureFormNavigationThrottle bypass
Ensure proper throttling of opaque embedded forms.
Fixed: 513702096
Change-Id: I6f0298581b49a29ce8743419044fcfbd5ade4a98
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8164181
Auto-Submit: Ari Chivukula <arichiv@chromium.org>
Commit-Queue: Carlos IL <carlosil@chromium.org>
Reviewed-by: Carlos IL <carlosil@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1669917}
---
diff --git a/components/security_interstitials/core/insecure_form_util.cc b/components/security_interstitials/core/insecure_form_util.cc
index a7fe5564..4ae57fe 100644
--- a/components/security_interstitials/core/insecure_form_util.cc
+++ b/components/security_interstitials/core/insecure_form_util.cc
@@ -43,8 +43,10 @@
bool IsInsecureFormActionOnSecureSource(const url::Origin& source_origin,
const GURL& action_url) {
- if (!base::EqualsCaseInsensitiveASCII(source_origin.scheme(),
- url::kHttpsScheme)) {
+ // For opaque origins the precursos tuple must be examined.
+ if (!base::EqualsCaseInsensitiveASCII(
+ source_origin.GetTupleOrPrecursorTupleIfOpaque().scheme(),
+ url::kHttpsScheme)) {
#if BUILDFLAG(IS_IOS)
// On iOS, tests can't use an HTTPS server that serves a valid HTTPS
// response. Check if the URL is treated as secure for testing purposes.
diff --git a/components/security_interstitials/core/insecure_form_util_unittest.cc b/components/security_interstitials/core/insecure_form_util_unittest.cc
index f62a178..3223b18 100644
--- a/components/security_interstitials/core/insecure_form_util_unittest.cc
+++ b/components/security_interstitials/core/insecure_form_util_unittest.cc
@@ -54,4 +54,19 @@
url::Origin::Create(GURL("http://127.0.0.1:123")),
GURL("http://example.com")));
#endif
+
+ // Opaque https source with insecure action still counts.
+ EXPECT_TRUE(IsInsecureFormActionOnSecureSource(
+ url::Origin::Create(GURL("https://example.com")).DeriveNewOpaqueOrigin(),
+ GURL("http://example.com")));
+
+ // Other combinations do not.
+ EXPECT_FALSE(IsInsecureFormActionOnSecureSource(
+ url::Origin::Create(GURL("https://example.com")).DeriveNewOpaqueOrigin(),
+ GURL("https://example.com")));
+ EXPECT_FALSE(IsInsecureFormActionOnSecureSource(
+ url::Origin::Create(GURL("http://example.com")).DeriveNewOpaqueOrigin(),
+ GURL("http://example.com")));
+ EXPECT_FALSE(IsInsecureFormActionOnSecureSource(url::Origin(),
+ GURL("http://example.com")));
}
Regression Test / PoC
diff --git a/components/security_interstitials/core/insecure_form_util_unittest.cc b/components/security_interstitials/core/insecure_form_util_unittest.cc
index f62a178..3223b18 100644
--- a/components/security_interstitials/core/insecure_form_util_unittest.cc
+++ b/components/security_interstitials/core/insecure_form_util_unittest.cc
@@ -54,4 +54,19 @@
url::Origin::Create(GURL("http://127.0.0.1:123")),
GURL("http://example.com")));
#endif
+
+ // Opaque https source with insecure action still counts.
+ EXPECT_TRUE(IsInsecureFormActionOnSecureSource(
+ url::Origin::Create(GURL("https://example.com")).DeriveNewOpaqueOrigin(),
+ GURL("http://example.com")));
+
+ // Other combinations do not.
+ EXPECT_FALSE(IsInsecureFormActionOnSecureSource(
+ url::Origin::Create(GURL("https://example.com")).DeriveNewOpaqueOrigin(),
+ GURL("https://example.com")));
+ EXPECT_FALSE(IsInsecureFormActionOnSecureSource(
+ url::Origin::Create(GURL("http://example.com")).DeriveNewOpaqueOrigin(),
+ GURL("http://example.com")));
+ EXPECT_FALSE(IsInsecureFormActionOnSecureSource(url::Origin(),
+ GURL("http://example.com")));
}
Original Bug Report
InsecureFormNavigationThrottle bypass via opaque initiator origin
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: A logic error in IsInsecureFormActionOnSecureSource fails to correctly identify secure origins when they are opaque, such as in sandboxed contexts. This allows an HTTPS page to potentially submit form data to an insecure HTTP endpoint without triggering the expected security interstitial warning.
Affected files:
components/security_interstitials/core/insecure_form_util.cccomponents/security_interstitials/content/insecure_form_navigation_throttle.cc
Estimated timestamp from git blame: 2020-10-06
Summary
Chrome’s InsecureFormNavigationThrottle is designed to protect users by displaying an interstitial warning when a secure (HTTPS) page attempts to submit a form to an insecure (HTTP) URL. This protection can potentially be bypassed if the initiating document has an opaque origin (e.g., due to a Content-Security-Policy: sandbox header or being inside a sandboxed <iframe>).
Technical Details
The issue resides in how the browser-side utility function IsInsecureFormActionOnSecureSource evaluates whether the initiator is a secure source. It checks the scheme of the origin directly using source_origin.scheme():
// components/security_interstitials/core/insecure_form_util.cc:44
bool IsInsecureFormActionOnSecureSource(const url::Origin& source_origin,
const GURL& action_url) {
if (!base::EqualsCaseInsensitiveASCII(source_origin.scheme(),
url::kHttpsScheme)) {
// On non-iOS platforms, returns false if scheme is not https
return false;
}
return IsInsecureFormAction(action_url);
}
For an opaque origin, url::Origin::scheme() is defined to return an empty string (url/origin.h:226-228), even if the origin has an HTTPS precursor. Consequently, the check source_origin.scheme() == url::kHttpsScheme fails, the function returns false, and the InsecureFormNavigationThrottle permits the navigation to proceed without showing the warning interstitial (components/security_interstitials/content/insecure_form_navigation_throttle.cc:132-136).
Impact
In its default configuration, Chrome does not degrade the ‘SECURE’ lock icon for mixed forms, relying instead on the submission-time interstitial to warn users. Because this interstitial is bypassed for opaque origins, a malicious HTTPS site could host a sandboxed page that appears secure but transmits sensitive user data (e.g., passwords or payment information) over unencrypted HTTP. Passive network observers could then intercept this data. Other protections, such as HttpsUpgradesInterceptor, do not apply to POST requests, and browser-side mixed content checks typically exempt main-frame navigations.
Potential Reproduction Steps
- Host a page over HTTPS with the following header:
Content-Security-Policy: sandbox allow-forms allow-scripts. - Include a form targeting an HTTP endpoint:
<form method='POST' action='http://example.com/collect'><input type='password' name='p' value='secret'></form>. - Trigger the form submission (e.g., via user interaction or script).
- Observe that the navigation proceeds directly over HTTP without a security interstitial, while the omnibox continues to display the secure lock icon.
Note: These steps are based on static code analysis; our tooling does not currently have the capability to run code to confirm the behavior.
Suggested Fix
The check in IsInsecureFormActionOnSecureSource should consider the precursor origin for opaque origins. This can be achieved by using GetTupleOrPrecursorTupleIfOpaque() instead of scheme(), which would align the browser-side logic with Blink’s MixedContentChecker:
if (!base::EqualsCaseInsensitiveASCII(source_origin.GetTupleOrPrecursorTupleIfOpaque().scheme(),
url::kHttpsScheme)) {
return false;
}
Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a
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.