RecvReloadWithHttpsOnlyException uses Top() unconditionally — sub-frame error page can downgrade top-level HTTPS to HTTP
Categories
(Core :: DOM: Security, defect)
Tracking
()
People
(Reporter: boiprcvn156, Unassigned)
Details
(Keywords: reporter-external)
Attachments
(1 file)
|
5.10 KB,
application/octet-stream
|
Details |
WindowGlobalParent::RecvReloadWithHttpsOnlyException() (dom/ipc/WindowGlobalParent.cpp:1644) uses BrowsingContext()->Top() unconditionally — it never checks whether the calling WindowGlobal actually belongs to the top-level frame. A sub-frame showing about:httpsonlyerror can call document.reloadWithHttpsOnlyException() and the handler will downgrade the top-level page from HTTPS to HTTP and add a session-duration HTTPS-Only exception for the top-level domain.
This is a logic bug, not a compromised-renderer scenario. The WebIDL gate CallerIsTrustedAboutHttpsOnlyError (Document.cpp:1505) checks IsAboutErrorPage(win, "httpsonlyerror"), which legitimately passes for a sub-frame that's on the error page. The problem is the handler then operates on Top() instead of the calling frame's own BC.
Root cause
// dom/ipc/WindowGlobalParent.cpp:1644
mozilla::ipc::IPCResult WindowGlobalParent::RecvReloadWithHttpsOnlyException() {
// Gets the TOP-LEVEL URI, not the calling frame's URI
nsCOMPtr<nsIURI> currentURI = BrowsingContext()->Top()->GetCurrentURI();
// Creates HTTP version of the TOP-LEVEL URI
(void)NS_MutateURI(innerURI).SetScheme("http"_ns).Finalize(getter_AddRefs(newURI));
// Adds exception for the TOP-LEVEL domain
rv = permMgr->AddFromPrincipal(principal, "https-only-load-insecure"_ns,
nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION,
nsIPermissionManager::EXPIRE_SESSION, 0);
// Navigates the TOP-LEVEL BC with system principal
loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
RefPtr<CanonicalBrowsingContext> topBC = BrowsingContext()->Top();
topBC->LoadURI(loadState, true);
}
No check: if (BrowsingContext() != BrowsingContext()->Top()) return IPC_FAIL(...);
Reproduction
Tested on debug build 155.0a1 with Marionette. Needs dom.security.https_only_mode = true.
- Enable HTTPS-Only mode
- Navigate to a real HTTPS site:
https://example.org/ - Inject an iframe pointing to an HTTP-only URL (something with no TLS):
let f = document.createElement('iframe'); f.src = 'http://SOMEHOST:9999/anything'; // port with no TLS listener document.body.appendChild(f); - HTTPS-Only mode upgrades the iframe load → TLS handshake fails →
about:httpsonlyerrorappears in the iframe - Switch into the iframe context and call:
document.reloadWithHttpsOnlyException(); - Check the top-level page: it's now on
http://example.org/ - Check permissions:
https-only-load-insecuresession permission was added for example.org
What I observed:
- BEFORE:
location.protocol = "https:", permission = 0 (no exception) - AFTER:
location.protocol = "http:", permission = 9 (LOAD_INSECURE_ALLOW_SESSION)
The top-level page was silently downgraded from HTTPS to HTTP, and the session exception means it stays that way for the rest of the browsing session.
Why this matters without renderer compromise
A page can embed iframes to HTTP-only resources. When HTTPS-Only mode shows the error page in those iframes, the "Continue to HTTP Site" button in any sub-frame error page will downgrade the top-level page — not just the iframe. A user who thinks they're allowing HTTP for some embedded resource is actually downgrading their main page.
With a compromised renderer it's even simpler — just call the method directly from the sub-frame, no user interaction needed.
Limitation
dom.security.https_only_mode defaults to false (opt-in). Only affects users who enabled it.
Suggested fix
Add at the top of RecvReloadWithHttpsOnlyException:
if (BrowsingContext() != BrowsingContext()->Top()) {
return IPC_FAIL(this, "Only top-level frames can add HTTPS-Only exceptions");
}
Comment 1•3 months ago
•
|
||
Thank you for the report. This issue has been determined to be a duplicate of bug 2014104.
Both report the same defect: WindowGlobalParent::RecvReloadWithHttpsOnlyException() uses BrowsingContext()->Top() unconditionally with no IsTop() guard, so a sub-frame showing about:httpsonlyerror can add a session https-only-load-insecure exception for the top-level origin and force the top-level page to reload over HTTP (system-principal navigation). Same handler, same missing check, same symptom.
Updated•3 months ago
|
Updated•2 months ago
|
Description
•