Chrome · FedCM
CVE-2026-87608
Logic Error in FedCM
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
TEST_Fcontent/common/webid/identity_url_loader_throttle_unittest.cc |
modified |
Files Changed
content/common/BUILD.gncontent/common/DEPScontent/common/webid/identity_url_loader_throttle.cccontent/common/webid/identity_url_loader_throttle_unittest.cc
Patch
From 4906428f67d6d47d3cb5341b7517389e889181ed Mon Sep 17 00:00:00 2001 From: Nicolás Peña <[email protected]> Date: Thu, 06 Aug 2026 07:53:37 -0700 Subject: [PATCH] [FedCM] Reject Set-Login headers on connections with cert errors IdentityUrlLoaderThrottle handles Set-Login response headers to set persistent origin-keyed IdP sign-in status. Currently, it checks whether the origin is potentially trustworthy, but does not check whether the connection has TLS certificate errors. An attacker MITM-ing an HTTPS connection with a self-signed or invalid certificate could cause Set-Login headers to be processed if the user clicked past the certificate error interstitial. This could persist origin-keyed sign-in status, leading to cross-site denial of service or state confusion. This CL early-returns in IdentityUrlLoaderThrottle when net::IsCertStatusError(response_head.cert_status) is true, matching the pattern used by other security-sensitive response headers. Bug: 540072282 Change-Id: I4693599760fa036b0c1194e84816e900f7bdd205 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8198686 Reviewed-by: Dave Tapuska <[email protected]> Reviewed-by: Yi Gu <[email protected]> Commit-Queue: Nicolás Peña <[email protected]> Cr-Commit-Position: refs/heads/main@{#1674972} --- diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index 264cdb4d..6db21bb 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn @@ -224,6 +224,7 @@ "//build:branding_buildflags", "//components/discardable_memory/common", "//components/input", + "//components/network_session_configurator/common", "//components/services/filesystem/public/mojom", "//components/tracing:tracing_config", "//content:content_resources", diff --git a/content/common/DEPS b/content/common/DEPS index fff9622..6379910 100644 --- a/content/common/DEPS +++ b/content/common/DEPS @@ -2,6 +2,7 @@ "-storage/browser", "+components/discardable_memory/common", + "+components/network_session_configurator/common", "+components/viz/common", "+device/base/synchronization", "+services/data_decoder/public/cpp", diff --git a/content/common/webid/identity_url_loader_throttle.cc b/content/common/webid/identity_url_loader_throttle.cc index 7f396480..c6e479e 100644 --- a/content/common/webid/identity_url_loader_throttle.cc +++ b/content/common/webid/identity_url_loader_throttle.cc @@ -8,14 +8,17 @@ #include <string_view> #include "base/auto_reset.h" +#include "base/command_line.h" #include "base/functional/bind.h" #include "base/strings/string_split.h" #include "base/task/sequenced_task_runner.h" #include "base/time/time.h" +#include "components/network_session_configurator/common/network_switches.h" #include "content/common/features.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" +#include "net/cert/cert_status_flags.h" #include "net/http/http_response_headers.h" #include "net/http/structured_headers.h" #include "services/data_decoder/public/cpp/data_decoder.h" @@ -139,6 +142,12 @@ return; } + if (net::IsCertStatusError(response_head.cert_status) && + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kIgnoreCertificateErrors)) { + return; + } + // TODO(crbug.com/40236764): // - Limit to toplevel frames // - Decide whether to limit to same-origin diff --git a/content/common/webid/identity_url_loader_throttle_unittest.cc b/content/common/webid/identity_url_loader_throttle_unittest.cc index da7a8475..b972da46 100644 --- a/content/common/webid/identity_url_loader_throttle_unittest.cc +++ b/content/common/webid/identity_url_loader_throttle_unittest.cc @@ -11,6 +11,7 @@ #include "content/public/common/content_features.h" #include "content/public/common/content_switches.h" #include "content/public/common/web_identity.h" +#include "net/cert/cert_status_flags.h" #include "net/http/http_response_headers.h" #include "net/http/structured_headers.h" #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" @@ -354,4 +355,29 @@ EXPECT_TRUE(result_item->item.is_integer()); } +TEST_F(IdentityUrlLoaderThrottleTest, CertError) { + TestDelegate delegate; + std::unique_ptr<blink::URLLoaderThrottle> throttle = + MaybeCreateIdentityUrlLoaderThrottle(CreateCallback(), + CreateParseCallback()); + ASSERT_NE(nullptr, throttle); + throttle->set_delegate(&delegate); + + network::ResourceRequest request; + request.url = GURL("https://accounts.idp.example/"); + bool defer = false; + + throttle->WillStartRequest(&request, &defer); + EXPECT_FALSE(defer); + + network::mojom::URLResponseHead response_head; + response_head.cert_status = net::CERT_STATUS_DATE_INVALID; + response_head.headers = net::HttpResponseHeaders::TryToCreate( + "HTTP/1.1 200 OK\nSet-Login: logged-in\n"); + throttle->WillProcessResponse(request.url, &response_head, &defer); + EXPECT_FALSE(defer); + + EXPECT_EQ(0, cb_num_calls_); +} + } // namespace content
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/content/common/webid/identity_url_loader_throttle_unittest.cc b/content/common/webid/identity_url_loader_throttle_unittest.cc
index da7a8475..b972da46 100644
--- a/content/common/webid/identity_url_loader_throttle_unittest.cc
+++ b/content/common/webid/identity_url_loader_throttle_unittest.cc
@@ -11,6 +11,7 @@
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/web_identity.h"
+#include "net/cert/cert_status_flags.h"
#include "net/http/http_response_headers.h"
#include "net/http/structured_headers.h"
#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
@@ -354,4 +355,29 @@
EXPECT_TRUE(result_item->item.is_integer());
}
+TEST_F(IdentityUrlLoaderThrottleTest, CertError) {
+ TestDelegate delegate;
+ std::unique_ptr<blink::URLLoaderThrottle> throttle =
+ MaybeCreateIdentityUrlLoaderThrottle(CreateCallback(),
+ CreateParseCallback());
+ ASSERT_NE(nullptr, throttle);
+ throttle->set_delegate(&delegate);
+
+ network::ResourceRequest request;
+ request.url = GURL("https://accounts.idp.example/");
+ bool defer = false;
+
+ throttle->WillStartRequest(&request, &defer);
+ EXPECT_FALSE(defer);
+
+ network::mojom::URLResponseHead response_head;
+ response_head.cert_status = net::CERT_STATUS_DATE_INVALID;
+ response_head.headers = net::HttpResponseHeaders::TryToCreate(
+ "HTTP/1.1 200 OK\nSet-Login: logged-in\n");
+ throttle->WillProcessResponse(request.url, &response_head, &defer);
+ EXPECT_FALSE(defer);
+
+ EXPECT_EQ(0, cb_num_calls_);
+}
+
} // namespace content
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page