Low watch CVSS 3.5 chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Low
Severity
3.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInappropriate implementation in Passwords
DescriptionInappropriate implementation in Passwords
ComponentPasswords
Sub-componentUI>Browser>Passwords
Bug ClassLogic Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker452071826
Fix commit98adca151821 (chromium/src) +222/-21
CVSS vectorCVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N
CISA KEVNot listed
CreditedAnonymous
Reported2025-10-14 (per vendor advisory)
Fix landed2025-10-23 (public git)
Disclosed2025-12-02
Patch gap40 days — fix public in source before the advisory

Background

HTTP auth autofill
when a site or proxy answers with an HTTP Basic/Digest challenge, Chrome shows a native login prompt, and the password manager can pre-fill it with a saved credential through ChromePasswordManagerClient::AutofillHttpAuth and HttpAuthManagerImpl::Autofill, which pass the username and password to an HttpAuthObserver via OnAutofillDataAvailable.
Biometric re-auth before filling
a password manager setting, checked through PasswordManagerClient::IsReauthBeforeFillingRequired, that requires the user to pass a device authentication (for example Touch ID or Windows Hello) through a device_reauth::DeviceAuthenticator before a saved password is filled.
`DeviceAuthenticator`
the platform abstraction that shows the OS authentication prompt (AuthenticateWithMessage) and reports a boolean result to a callback; it can be cancelled with Cancel().
`content::Visibility::HIDDEN`
the visibility state of a WebContents whose tab is not currently shown to the user.

Root Cause Analysis

Before the patch, HttpAuthManagerImpl::Autofill checked only that an observer was attached, that the form manager matched, and that client_->IsFillingEnabled(...). It then called observer_->OnAutofillDataAvailable(...) with the stored username and password straight away. It never consulted IsReauthBeforeFillingRequired, so the HTTP auth prompt was filled without the biometric check that the user had asked for. HTML form filling enforces that check, so the HTTP auth path was an inconsistent bypass. ChromePasswordManagerClient::AutofillHttpAuth also filled unconditionally, even when the tab was hidden, and then called PasswordWasAutofilled right away.

The fix adds an early return when web_contents()->GetVisibility() == content::Visibility::HIDDEN. It also makes HttpAuthManagerImpl::Autofill fetch a DeviceAuthenticator and, when re-auth is required, defer filling to OnReauthCompleted. That function hands the credentials to the observer only if auth_result is true. PasswordWasAutofilled is now passed as a completion callback, with a copy of the best matches, because the form manager may not outlive the authentication.

Key insight
The HTTP Basic/Digest auth autofill path released saved credentials without honouring the user’s “re-authenticate before filling” requirement, and it also filled them in hidden tabs. The fix gates the fill behind a DeviceAuthenticator prompt and refuses to fill when the tab is not visible.

Attack Path

  1. Trigger an HTTP auth challenge a page, or a resource it loads, gets a 401/407 challenge for a realm for which the victim has a saved credential, so the browser builds an HTTP auth form manager and calls ChromePasswordManagerClient::AutofillHttpAuth.
  2. Skip the re-auth gate before the fix, HttpAuthManagerImpl::Autofill passed the stored username and password to the auth prompt observer without calling IsReauthBeforeFillingRequired or showing a DeviceAuthenticator prompt.
  3. Fill while not visible the old AutofillHttpAuth did not check tab visibility, so the fill could also happen in a background (hidden) tab that the user could not see.
  4. Credential exposure someone at the browser, or a flow that submits the filled prompt, could get the saved credential without passing the biometric check the user had enabled (inferred).

Impact Assessment

This bypasses a user-chosen protection: saved HTTP Basic/Digest credentials are filled into the browser’s auth prompt in the browser process without the device re-authentication the user configured, and possibly in a hidden tab. The patch only matters when a credential is saved for the challenged realm and re-auth-before-filling is enabled. It does not show what that setting defaults to on each platform. Turning the bypass into credential theft likely needs local physical access to an unlocked session, or a user who submits the pre-filled prompt (inferred). The vendor rates it low severity (“Inappropriate implementation in Passwords”), and the advisory data given does not report exploitation in the wild.

Changed Functions

FunctionChangeNotes
if
components/password_manager/core/browser/http_auth_manager_impl.cc
modified
DeviceAuthenticator
components/password_manager/core/browser/http_auth_manager_impl.h
modified
PasswordManagerClient
components/password_manager/core/browser/http_auth_manager_impl.h
modified

Files Changed

  • chrome/browser/password_manager/chrome_password_manager_client.cc
  • components/password_manager/core/browser/http_auth_manager_impl.cc
  • components/password_manager/core/browser/http_auth_manager_impl.h
  • components/password_manager/core/browser/http_auth_manager_unittest.cc

Audit Directions

  • Parallel fill paths skipping re-auth
    check every credential-release path (HTTP auth, manual fallback, the account/keyboard accessory, touch-to-fill, the credential management API, password export/view) for the same IsReauthBeforeFillingRequired / DeviceAuthenticator gate that HTML form filling uses.
  • Visibility / focus gating
    look for autofill or credential-surfacing code that acts on background, hidden or prerendered WebContents without a visibility check.
  • Async auth callbacks and lifetime
    where filling becomes asynchronous behind an auth prompt, make sure callbacks are bound with weak pointers, that copied state does not depend on objects that may die (such as PasswordFormManagerForUI), and that pending authenticators are cancelled on teardown.
From 98adca1518214906f219902deb50aa01073db37f Mon Sep 17 00:00:00 2001
From: Viktor Semeniuk <[email protected]>
Date: Thu, 23 Oct 2025 03:17:47 -0700
Subject: [PATCH] Prompt biometric reauth before filling password for basic auth

Fixed: 452071826
Change-Id: I3b91a14f3ef5ace86de024d54178356c14d04dfc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7046232
Commit-Queue: Viktor Semeniuk <[email protected]>
Reviewed-by: Vasilii Sukhanov <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1534201}
---

diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc
index 840dee7..a851eac 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -912,11 +912,27 @@
 void ChromePasswordManagerClient::AutofillHttpAuth(
     const PasswordForm& preferred_match,
     const password_manager::PasswordFormManagerForUI* form_manager) {
-  httpauth_manager_.Autofill(preferred_match, form_manager);
-  DCHECK(!form_manager->GetBestMatches().empty());
-  PasswordWasAutofilled(form_manager->GetBestMatches(),
-                        url::Origin::Create(form_manager->GetURL()), {},
-                        /*was_autofilled_on_pageload=*/false);
+  if (web_contents()->GetVisibility() == content::Visibility::HIDDEN) {
+    // Do not autofill credentials if current tab is not visible.
+    return;
+  }
+
+  CHECK(!form_manager->GetBestMatches().empty());
+
+  // Make a copy of best matches as form_manager is not guaranteed to outlive
+  // authentication.
+  std::vector<PasswordForm> best_matches;
+  for (const auto& result : form_manager->GetBestMatches()) {
+    best_matches.emplace_back(result);
+  }
+
+  httpauth_manager_.Autofill(
+      preferred_match, form_manager,
+      base::BindOnce(&ChromePasswordManagerClient::PasswordWasAutofilled,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(best_matches),
+                     url::Origin::Create(form_manager->GetURL()),
+                     base::span<const PasswordForm>(),
+                     /*was_autofilled_on_pageload=*/false));
 }
 
 void ChromePasswordManagerClient::NotifyUserCredentialsWereLeaked(
diff --git a/components/password_manager/core/browser/http_auth_manager_impl.cc b/components/password_manager/core/browser/http_auth_manager_impl.cc
index 3853370..941e30b 100644
--- a/components/password_manager/core/browser/http_auth_manager_impl.cc
+++ b/components/password_manager/core/browser/http_auth_manager_impl.cc
@@ -6,7 +6,9 @@
 
 #include <utility>
 
+#include "base/task/single_thread_task_runner.h"
 #include "components/autofill/core/common/save_password_progress_logger.h"
+#include "components/device_reauth/device_authenticator.h"
 #include "components/password_manager/core/browser/password_form.h"
 #include "components/password_manager/core/browser/password_form_manager.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
@@ -36,6 +38,9 @@
   if (observer_) {
     observer_->OnLoginModelDestroying();
   }
+  if (authenticator_) {
+    authenticator_->Cancel();
+  }
 }
 
 void HttpAuthManagerImpl::DetachObserver(HttpAuthObserver* observer) {
@@ -75,14 +80,57 @@
   }
 }
 
-void HttpAuthManagerImpl::Autofill(
-    const PasswordForm& preferred_match,
-    const PasswordFormManagerForUI* form_manager) const {
-  DCHECK_NE(PasswordForm::Scheme::kHtml, preferred_match.scheme);
-  if (observer_ && (form_manager_.get() == form_manager) &&
-      client_->IsFillingEnabled(form_manager_->GetURL())) {
+void HttpAuthManagerImpl::Autofill(const PasswordForm& preferred_match,
+                                   const PasswordFormManagerForUI* form_manager,
+                                   base::OnceClosure on_filling_complete) {
+  CHECK_NE(PasswordForm::Scheme::kHtml, preferred_match.scheme);
+  if (!observer_ || form_manager_.get() != form_manager) {
+    return;
+  }
+
+  if (!client_->IsFillingEnabled(form_manager_->GetURL())) {
+    return;
+  }
+
+  std::unique_ptr<device_reauth::DeviceAuthenticator> authenticator =
+      client_->GetDeviceAuthenticator();
+
+  // Biometric filling is disabled, notify observers and invoke callback.
+  if (!client_->IsReauthBeforeFillingRequired(authenticator.get())) {
     observer_->OnAutofillDataAvailable(preferred_match.username_value,
                                        preferred_match.password_value);
+    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
+        FROM_HERE, std::move(on_filling_complete));
+    return;
+  }
+
+  auto filling_callback = base::BindOnce(
+      &HttpAuthManagerImpl::OnReauthCompleted, weak_ptr_factory_.GetWeakPtr(),
+      preferred_match.username_value, preferred_match.password_value,
+      std::move(on_filling_complete));
+  if (authenticator_) {
+    authenticator_->Cancel();
+  }
+  authenticator_ = std::move(authenticator);
+
+  std::u16string message;
+#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
+  const std::u16string origin = base::UTF8ToUTF16(
+      GetShownOrigin(url::Origin::Create(client_->GetLastCommittedURL())));
+  message =
+      l10n_util::GetStringFUTF16(IDS_PASSWORD_MANAGER_FILLING_REAUTH, origin);
+#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_CHROMEOS)
+  authenticator_->AuthenticateWithMessage(message, std::move(filling_callback));
+}
+
+void HttpAuthManagerImpl::OnReauthCompleted(
+    const std::u16string& username,
+    const std::u16string& password,
+    base::OnceClosure on_filling_complete,
+    bool auth_result) {
+  if (auth_result) {
+    observer_->OnAutofillDataAvailable(username, password);
+    std::move(on_filling_complete).Run();
   }
 }
 
diff --git a/components/password_manager/core/browser/http_auth_manager_impl.h b/components/password_manager/core/browser/http_auth_manager_impl.h
index 3bc849e5..c3248d1 100644
--- a/components/password_manager/core/browser/http_auth_manager_impl.h
+++ b/components/password_manager/core/browser/http_auth_manager_impl.h
@@ -13,6 +13,10 @@
 #include "components/password_manager/core/browser/http_auth_manager.h"
 #include "components/password_manager/core/browser/http_auth_observer.h"
 
+namespace device_reauth {
+class DeviceAuthenticator;
+}
+
 namespace password_manager {
 
 class PasswordManagerClient;
@@ -40,11 +44,14 @@
   void OnPasswordFormDismissed() override;
 
   // Called by a PasswordManagerClient when it decides that a HTTP auth dialog
-  // can be auto-filled. It notifies the observer about new credentials given
+  // can be auto-filled. If biometric auth before filling is enabled prompt
+  // authentication first. It notifies the observer about new credentials given
   // that the form manged by |form_manager| equals the one observed by the
-  // observer that is managed by |form_manager|.
+  // observer that is managed by |form_manager|. |on_filling_complete| is
+  // invoked after filling is completed.
   void Autofill(const PasswordForm& preferred_match,
-                const PasswordFormManagerForUI* form_manager) const;
+                const PasswordFormManagerForUI* form_manager,
+                base::OnceClosure on_filling_complete);
 
   // Handles successful navigation to the main frame.
   void OnDidFinishMainFrameNavigation();
@@ -60,6 +67,11 @@
   // Initiates the saving of the password.
   void OnLoginSuccesfull();
 
+  void OnReauthCompleted(const std::u16string& username,
+                         const std::u16string& password,
+                         base::OnceClosure on_filling_complete,
+                         bool auth_result);
+
   // The embedder-level client. Must outlive this class.
   const raw_ptr<PasswordManagerClient> client_;
 
@@ -72,6 +84,11 @@
   // When set to true, the password form has been dismissed and |form_manager_|
   // will be cleared on next navigation.
   bool form_dismissed_ = false;
+
+  // The authenticator used to trigger a biometric re-auth before filling.
+  std::unique_ptr<device_reauth::DeviceAuthenticator> authenticator_;
+
+  base::WeakPtrFactory<HttpAuthManagerImpl> weak_ptr_factory_{this};
 };
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/http_auth_manager_unittest.cc b/components/password_manager/core/browser/http_auth_manager_unittest.cc
index 90687dd..04bba040 100644
--- a/components/password_manager/core/browser/http_auth_manager_unittest.cc
+++ b/components/password_manager/core/browser/http_auth_manager_unittest.cc
@@ -10,13 +10,17 @@
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/components/password_manager/core/browser/http_auth_manager_unittest.cc b/components/password_manager/core/browser/http_auth_manager_unittest.cc
index 90687dd..04bba040 100644
--- a/components/password_manager/core/browser/http_auth_manager_unittest.cc
+++ b/components/password_manager/core/browser/http_auth_manager_unittest.cc
@@ -10,13 +10,17 @@
 #include "base/feature_list.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/gmock_callback_support.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/metrics/user_action_tester.h"
+#include "base/test/mock_callback.h"
 #include "base/test/task_environment.h"
+#include "base/test/test_future.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
 #include "components/autofill/core/common/form_field_data.h"
+#include "components/device_reauth/mock_device_authenticator.h"
 #include "components/password_manager/core/browser/form_fetcher_impl.h"
 #include "components/password_manager/core/browser/http_auth_manager_impl.h"
 #include "components/password_manager/core/browser/password_form.h"
@@ -70,6 +74,14 @@
               (const, override));
   MOCK_METHOD(void, PromptUserToSaveOrUpdatePasswordPtr, (), ());
   MOCK_METHOD(PrefService*, GetPrefs, (), (const, override));
+  MOCK_METHOD(bool,
+              IsReauthBeforeFillingRequired,
+              (device_reauth::DeviceAuthenticator*),
+              (override));
+  MOCK_METHOD(std::unique_ptr<device_reauth::DeviceAuthenticator>,
+              GetDeviceAuthenticator,
+              (),
+              (override));
 
   // Workaround for std::unique_ptr<> lacking a copy constructor.
   bool PromptUserToSaveOrUpdatePassword(
@@ -138,10 +150,6 @@
     httpauth_manager_ = std::make_unique<HttpAuthManagerImpl>(&client_);
 
     EXPECT_CALL(*store_, IsAbleToSavePasswords()).WillRepeatedly(Return(true));
-
-    ON_CALL(client_, AutofillHttpAuth(_, _))
-        .WillByDefault(
-            Invoke(httpauth_manager_.get(), &HttpAuthManagerImpl::Autofill));
   }
 
   HttpAuthManagerImpl* httpauth_manager() { return httpauth_manager_.get(); }
@@ -155,8 +163,18 @@
   std::unique_ptr<HttpAuthManagerImpl> httpauth_manager_;
 };
 
-TEST_P(HttpAuthManagerTest, HttpAuthFilling) {
-  EXPECT_CALL(client_, IsFillingEnabled(_)).WillRepeatedly(Return(true));
+TEST_P(HttpAuthManagerTest, HttpAuthFillingReauthNotRequired) {
+  EXPECT_CALL(client_, IsFillingEnabled).WillRepeatedly(Return(true));
+  EXPECT_CALL(client_, IsReauthBeforeFillingRequired)
+      .WillRepeatedly(Return(false));
+  base::test::TestFuture<void> future;
+  EXPECT_CALL(client_, AutofillHttpAuth)
+      .WillRepeatedly(
+          [&](const password_manager::PasswordForm& preferred_match,
+              const password_manager::PasswordFormManagerForUI* form_manager) {
+            httpauth_manager()->Autofill(preferred_match, form_manager,
+                                         future.GetCallback());
+          });
 
   PasswordForm observed_form;
   observed_form.scheme = PasswordForm::Scheme::kBasic;
@@ -170,17 +188,119 @@
   MockHttpAuthObserver observer;
 
   base::WeakPtr<PasswordStoreConsumer> consumer;
-  EXPECT_CALL(*store_, GetLogins(_, _)).WillOnce(SaveArg<1>(&consumer));
+  EXPECT_CALL(*store_, GetLogins).WillOnce(SaveArg<1>(&consumer));
+  httpauth_manager()->SetObserverAndDeliverCredentials(&observer,
+                                                       observed_form);
+  EXPECT_CALL(observer, OnAutofillDataAvailable(std::u16string_view(u"user"),
+                                                std::u16string_view(u"1234")))
+      .Times(2);
+  ASSERT_TRUE(consumer);
+  std::vector<PasswordForm> result;
+  result.push_back(stored_form);
+  consumer->OnGetPasswordStoreResultsOrErrorFrom(store_.get(),
+                                                 std::move(result));
+  ASSERT_TRUE(future.Wait());
+
+  testing::Mock::VerifyAndClearExpectations(&store_);
+  httpauth_manager()->DetachObserver(&observer);
+}
+
+// Test autofill when biometric re-auth is required and successful.
+TEST_P(HttpAuthManagerTest, HttpAuthFillingReauthSuccess) {
+  EXPECT_CALL(client_, IsFillingEnabled).WillRepeatedly(Return(true));
+  EXPECT_CALL(client_, IsReauthBeforeFillingRequired)
+      .WillRepeatedly(Return(true));
+  base::MockOnceClosure mock_callback;
+  EXPECT_CALL(client_, AutofillHttpAuth)
+      .WillOnce(
+          [&](const password_manager::PasswordForm& preferred_match,
+              const password_manager::PasswordFormManagerForUI* form_manager) {
+            httpauth_manager()->Autofill(preferred_match, form_manager,
+                                         mock_callback.Get());
+          });
+
+  auto mock_authenticator =
+      std::make_unique<device_reauth::MockDeviceAuthenticator>();
+  EXPECT_CALL(*mock_authenticator, AuthenticateWithMessage)
+      .WillOnce(base::test::RunOnceCallback<1>(true));
+  EXPECT_CALL(client_, GetDeviceAuthenticator)
+      .WillOnce(Return(testing::ByMove(std::move(mock_authenticator))));
+
+  PasswordForm observed_form;
+  observed_form.scheme = PasswordForm::Scheme::kBasic;
+  observed_form.url = GURL("http://proxy.com/");
+  observed_form.signon_realm = "proxy.com/realm";
+
+  PasswordForm stored_form = observed_form;
+  stored_form.username_value = u"user";
+  stored_form.password_value = u"1234";
+
+  MockHttpAuthObserver observer;
+
+  base::WeakPtr<PasswordStoreConsumer> consumer;
+  EXPECT_CALL(*store_, GetLogins).WillOnce(SaveArg<1>(&consumer));
   httpauth_manager()->SetObserverAndDeliverCredentials(&observer,
                                                        observed_form);
   EXPECT_CALL(observer, OnAutofillDataAvailable(std::u16string_view(u"user"),
                                                 std::u16string_view(u"1234")));
+  EXPECT_CALL(mock_callback, Run);
   ASSERT_TRUE(consumer);
   std::vector<PasswordForm> result;
   result.push_back(stored_form);
   consumer->OnGetPasswordStoreResultsOrErrorFrom(store_.get(),
                                                  std::move(result));
   testing::Mock::VerifyAndClearExpectations(&store_);
+
+  httpauth_manager()->DetachObserver(&observer);
+}
+
+// Test autofill when biometric re-auth is required and successful.
+TEST_P(HttpAuthManagerTest, HttpAuthFillingReauthFailure) {
+  EXPECT_CALL(client_, IsFillingEnabled).WillRepeatedly(Return(true));
+  EXPECT_CALL(client_, IsReauthBeforeFillingRequired)
+      .WillRepeatedly(Return(true));
+  base::MockOnceClosure mock_callback;
+  EXPECT_CALL(client_, AutofillHttpAuth)
+      .WillOnce(
+          [&](const password_manager::PasswordForm& preferred_match,
+              const password_manager::PasswordFormManagerForUI* form_manager) {
+            httpauth_manager()->Autofill(preferred_match, form_manager,
+                                         mock_callback.Get());
+          });
+
+  auto mock_authenticator =
+      std::make_unique<device_reauth::MockDeviceAuthenticator>();
+  EXPECT_CALL(*mock_authenticator, AuthenticateWithMessage)
+      .WillOnce(base::test::RunOnceCallback<1>(false));
+
+  EXPECT_CALL(client_, GetDeviceAuthenticator)
+      .WillOnce(Return(testing::ByMove(std::move(mock_authenticator))));
+
+  PasswordForm observed_form;
+  observed_form.scheme = PasswordForm::Scheme::kBasic;
+  observed_form.url = GURL("http://proxy.com/");
+  observed_form.signon_realm = "proxy.com/realm";
+
+  PasswordForm stored_form = observed_form;
+  stored_form.username_value = u"user";
+  stored_form.password_value = u"1234";
+
+  MockHttpAuthObserver observer;
+
+  base::WeakPtr<PasswordStoreConsumer> consumer;
+  EXPECT_CALL(*store_, GetLogins).WillOnce(SaveArg<1>(&consumer));
+  httpauth_manager()->SetObserverAndDeliverCredentials(&observer,
+                                                       observed_form);
+  EXPECT_CALL(observer, OnAutofillDataAvailable).Times(0);
+  EXPECT_CALL(mock_callback, Run).Times(0);
+
+  ASSERT_TRUE(consumer);
+  std::vector<PasswordForm> result;
+  result.push_back(stored_form);
+  consumer->OnGetPasswordStoreResultsOrErrorFrom(store_.get(),
+                                                 std::move(result));
+  testing::Mock::VerifyAndClearExpectations(&store_);
+
   httpauth_manager()->DetachObserver(&observer);
 }
Loading diff…

Original Bug Report

reported by [email protected]

HTTP-Auth Passwords are not secured on MacOS

Security Bug

Important: Please do not change the component of this bug manually.

Please READ THIS FAQ before filing a bug: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/security/faq.md

Please see the following link for instructions on filing security bugs: https://www.chromium.org/Home/chromium-security/reporting-security-bugs

Reports may be eligible for reward payments under the Chrome VRP: https://g.co/chrome/vrp

NOTE: Security bugs are normally made public once a fix has been widely deployed.


VULNERABILITY DETAILS “Use your screen lock when filling passwords” on MacOS does not work for HTTP Authentication.

VERSION Chrome Version: 141.0.7390.77 stable Operating System: MacOS Tahoe 26.0 (25A354)

REPRODUCTION CASE

  1. Install Chrome freshly
  2. Open Chrome
  3. Open the URL chrome://password-manager/settings
  4. Enable the Setting “Use your screen lock when filling passwords”
  5. Open the URL https://authenticationtest.com/HTTPAuth/
  6. Enter “user” and “pass” as credentials
  7. Save the credentials as supposed by Chrome
  8. Clear website data to log out, close and reopen Chrome
  9. Open again the URL https://authenticationtest.com/HTTPAuth/

At this point, the passwords are already filled in and we can log in without the need to authenticate via screen lock (e.g. fingerprint)

Conversely, the screen lock to access passwords works on other authentication methods such as the one on this site: https://authenticationtest.com/simpleFormAuth/

CREDIT INFORMATION Externally reported security bugs may appear in Chrome release notes. If this bug is included, how would you like to be credited? Reporter credit: Anonymous

View on issue tracker