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 <npm@chromium.org>
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 <dtapuska@chromium.org>
Reviewed-by: Yi Gu <yigu@chromium.org>
Commit-Queue: Nicolás Peña <npm@chromium.org>
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