Firefox · DOM
CVE-2025-6433
Logic Error in DOM
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
switchdom/webauthn/WebAuthnUtil.cpp |
modified | |
ifdom/webauthn/tests/browser/browser_webauthn_cert_override.js |
modified |
Files Changed
dom/webauthn/WebAuthnTransactionParent.cppdom/webauthn/WebAuthnUtil.cppdom/webauthn/WebAuthnUtil.hdom/webauthn/tests/browser/browser.tomldom/webauthn/tests/browser/browser_webauthn_cert_override.js
Patch
diff --git a/dom/webauthn/WebAuthnTransactionParent.cpp b/dom/webauthn/WebAuthnTransactionParent.cpp
index 3c704b9620e..f83880bc825 100644
--- a/dom/webauthn/WebAuthnTransactionParent.cpp
+++ b/dom/webauthn/WebAuthnTransactionParent.cpp
@@ -121,6 +121,11 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestRegister(
WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
nsIPrincipal* principal = manager->DocumentPrincipal();
+ if (!IsWebAuthnAllowedForTransportSecurityInfo(manager->GetSecurityInfo())) {
+ aResolver(NS_ERROR_DOM_SECURITY_ERR);
+ return IPC_OK();
+ }
+
if (!IsWebAuthnAllowedForPrincipal(principal)) {
aResolver(NS_ERROR_DOM_SECURITY_ERR);
return IPC_OK();
@@ -311,6 +316,11 @@ mozilla::ipc::IPCResult WebAuthnTransactionParent::RecvRequestSign(
WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
nsIPrincipal* principal = manager->DocumentPrincipal();
+ if (!IsWebAuthnAllowedForTransportSecurityInfo(manager->GetSecurityInfo())) {
+ aResolver(NS_ERROR_DOM_SECURITY_ERR);
+ return IPC_OK();
+ }
+
if (!IsWebAuthnAllowedForPrincipal(principal)) {
aResolver(NS_ERROR_DOM_SECURITY_ERR);
return IPC_OK();
diff --git a/dom/webauthn/WebAuthnUtil.cpp b/dom/webauthn/WebAuthnUtil.cpp
index 5fb65ac33ce..96d20e6fbf3 100644
--- a/dom/webauthn/WebAuthnUtil.cpp
+++ b/dom/webauthn/WebAuthnUtil.cpp
@@ -132,6 +132,28 @@ bool IsWebAuthnAllowedForPrincipal(const nsCOMPtr<nsIPrincipal>& aPrincipal) {
return true;
}
+bool IsWebAuthnAllowedForTransportSecurityInfo(
+ nsITransportSecurityInfo* aSecurityInfo) {
+ nsITransportSecurityInfo::OverridableErrorCategory overridableErrorCategory;
+ if (!aSecurityInfo || NS_FAILED(aSecurityInfo->GetOverridableErrorCategory(
+ &overridableErrorCategory))) {
+ return false;
+ }
+
+ switch (overridableErrorCategory) {
+ case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET:
+ return true;
+ case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME:
+ return true;
+ case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST:
+ return false;
+ case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN:
+ return false;
+ default:
+ return false;
+ }
+}
+
bool IsValidRpId(const nsCOMPtr<nsIPrincipal>& aPrincipal,
const nsACString& aRpId) {
// This checks two of the conditions defined in
diff --git a/dom/webauthn/WebAuthnUtil.h b/dom/webauthn/WebAuthnUtil.h
index 68f50a2dcff..0806db47e06 100644
--- a/dom/webauthn/WebAuthnUtil.h
+++ b/dom/webauthn/WebAuthnUtil.h
@@ -19,6 +19,9 @@ bool IsWebAuthnAllowedInDocument(const nsCOMPtr<Document>& aDoc);
bool IsWebAuthnAllowedForPrincipal(const nsCOMPtr<nsIPrincipal>& aPrincipal);
+bool IsWebAuthnAllowedForTransportSecurityInfo(
+ nsITransportSecurityInfo* aSecurityInfo);
+
nsresult DefaultRpId(const nsCOMPtr<nsIPrincipal>& aPrincipal,
/* out */ nsACString& aRpId);
diff --git a/dom/webauthn/tests/browser/browser.toml b/dom/webauthn/tests/browser/browser.toml
index 9bca41566ef..0aaeb5eb3c4 100644
--- a/dom/webauthn/tests/browser/browser.toml
+++ b/dom/webauthn/tests/browser/browser.toml
@@ -21,6 +21,8 @@ run-if = ["os != 'win'"] # Test not relevant on 1903+
["browser_fido_appid_extension.js"]
run-if = ["os != 'win'"] # Test not relevant on 1903+
+["browser_webauthn_cert_override.js"]
+
["browser_webauthn_conditional_mediation.js"]
["browser_webauthn_ipaddress.js"]
diff --git a/dom/webauthn/tests/browser/browser_webauthn_cert_override.js b/dom/webauthn/tests/browser/browser_webauthn_cert_override.js
new file mode 100644
index 00000000000..abe77d95523
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser_webauthn_cert_override.js
@@ -0,0 +1,89 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+add_virtual_authenticator();
+
+let expectSecurityError = expectError("Security");
+
+async function test_webauthn_with_cert_override(
+ aTestDomain,
+ aExpectSecurityError
+) {
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+
+ let testURL = "https://" + aTestDomain;
+ let certErrorLoaded;
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ () => {
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, testURL);
+ let browser = gBrowser.selectedBrowser;
+ certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ },
+ false
+ );
+ info("Waiting for cert error page.");
+ await certErrorLoaded;
+
+ let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ info("Adding certificate error override.");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ let doc = content.document;
+ let exceptionButton = doc.getElementById("exceptionDialogButton");
+ exceptionButton.click();
+ });
+
+ info("Waiting for page load.");
+ await loaded;
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ let doc = content.document;
+ ok(
+ !doc.documentURI.startsWith("about:certerror"),
+ "Exception has been added."
+ );
+ });
+
+ let makeCredPromise = promiseWebAuthnMakeCredential(tab, "none", "preferred");
+ if (aExpectSecurityError) {
+ await makeCredPromise.then(arrivingHereIsBad).catch(expectSecurityError);
+ ok(
+ true,
+ "Calling navigator.credentials.create() results in a security error"
+ );
+ } else {
+ await makeCredPromise.catch(arrivingHereIsBad);
+ ok(true, "Calling navigator.credentials.create() is allowed");
+ }
+
+ let getAssertionPromise = promiseWebAuthnGetAssertionDiscoverable(tab);
+ if (aExpectSecurityError) {
+ await getAssertionPromise
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError);
+ ok(true, "Calling navigator.credentials.get() results in a security error");
+ } else {
+ await getAssertionPromise.catch(arrivingHereIsBad);
+ ok(true, "Calling navigator.credentials.get() results in a security error");
+ }
+
+ certOverrideService.clearValidityOverride(aTestDomain, -1, {});
+
+ loaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
+ BrowserCommands.reloadSkipCache();
+ await loaded;
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+}
+
+add_task(() => test_webauthn_with_cert_override("expired.example.com", false));
+add_task(() => test_webauthn_with_cert_override("untrusted.example.com", true));
+add_task(() =>
+ test_webauthn_with_cert_override("no-subject-alt-name.example.com", true)
+);
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/dom/webauthn/tests/browser/browser.toml b/dom/webauthn/tests/browser/browser.toml
index 9bca41566ef..0aaeb5eb3c4 100644
--- a/dom/webauthn/tests/browser/browser.toml
+++ b/dom/webauthn/tests/browser/browser.toml
@@ -21,6 +21,8 @@ run-if = ["os != 'win'"] # Test not relevant on 1903+
["browser_fido_appid_extension.js"]
run-if = ["os != 'win'"] # Test not relevant on 1903+
+["browser_webauthn_cert_override.js"]
+
["browser_webauthn_conditional_mediation.js"]
["browser_webauthn_ipaddress.js"]
diff --git a/dom/webauthn/tests/browser/browser_webauthn_cert_override.js b/dom/webauthn/tests/browser/browser_webauthn_cert_override.js
new file mode 100644
index 00000000000..abe77d95523
--- /dev/null
+++ b/dom/webauthn/tests/browser/browser_webauthn_cert_override.js
@@ -0,0 +1,89 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+add_virtual_authenticator();
+
+let expectSecurityError = expectError("Security");
+
+async function test_webauthn_with_cert_override(
+ aTestDomain,
+ aExpectSecurityError
+) {
+ let certOverrideService = Cc[
+ "@mozilla.org/security/certoverride;1"
+ ].getService(Ci.nsICertOverrideService);
+
+ let testURL = "https://" + aTestDomain;
+ let certErrorLoaded;
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ () => {
+ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, testURL);
+ let browser = gBrowser.selectedBrowser;
+ certErrorLoaded = BrowserTestUtils.waitForErrorPage(browser);
+ },
+ false
+ );
+ info("Waiting for cert error page.");
+ await certErrorLoaded;
+
+ let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ info("Adding certificate error override.");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ let doc = content.document;
+ let exceptionButton = doc.getElementById("exceptionDialogButton");
+ exceptionButton.click();
+ });
+
+ info("Waiting for page load.");
+ await loaded;
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ let doc = content.document;
+ ok(
+ !doc.documentURI.startsWith("about:certerror"),
+ "Exception has been added."
+ );
+ });
+
+ let makeCredPromise = promiseWebAuthnMakeCredential(tab, "none", "preferred");
+ if (aExpectSecurityError) {
+ await makeCredPromise.then(arrivingHereIsBad).catch(expectSecurityError);
+ ok(
+ true,
+ "Calling navigator.credentials.create() results in a security error"
+ );
+ } else {
+ await makeCredPromise.catch(arrivingHereIsBad);
+ ok(true, "Calling navigator.credentials.create() is allowed");
+ }
+
+ let getAssertionPromise = promiseWebAuthnGetAssertionDiscoverable(tab);
+ if (aExpectSecurityError) {
+ await getAssertionPromise
+ .then(arrivingHereIsBad)
+ .catch(expectSecurityError);
+ ok(true, "Calling navigator.credentials.get() results in a security error");
+ } else {
+ await getAssertionPromise.catch(arrivingHereIsBad);
+ ok(true, "Calling navigator.credentials.get() results in a security error");
+ }
+
+ certOverrideService.clearValidityOverride(aTestDomain, -1, {});
+
+ loaded = BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
+ BrowserCommands.reloadSkipCache();
+ await loaded;
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+}
+
+add_task(() => test_webauthn_with_cert_override("expired.example.com", false));
+add_task(() => test_webauthn_with_cert_override("untrusted.example.com", true));
+add_task(() =>
+ test_webauthn_with_cert_override("no-subject-alt-name.example.com", true)
+);
Loading diff…
References
On This Page