/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */
/* static */ bool nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(bool aFromPrivateWindow) { // if the general pref is set to true, then we always return if (mozilla::StaticPrefs::dom_security_https_only_mode()) { returntrue;
}
// otherwise we check if executing in private browsing mode and return true // if the PBM pref for HTTPS-Only is set. if (aFromPrivateWindow &&
mozilla::StaticPrefs::dom_security_https_only_mode_pbm()) { returntrue;
} returnfalse;
}
/* static */ bool nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(bool aFromPrivateWindow) { // HTTPS-Only takes priority over HTTPS-First if (IsHttpsOnlyModeEnabled(aFromPrivateWindow)) { returnfalse;
}
// if the general pref is set to true, then we always return if (mozilla::StaticPrefs::dom_security_https_first()) { returntrue;
}
// otherwise we check if executing in private browsing mode and return true // if the PBM pref for HTTPS-First is set. if (aFromPrivateWindow &&
mozilla::StaticPrefs::dom_security_https_first_pbm()) { returntrue;
} returnfalse;
}
/* static */ void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(
mozilla::net::DocumentLoadListener* aDocumentLoadListener) { // only send http background request to counter timeouts if the // pref allows us to do that. if (!mozilla::StaticPrefs::
dom_security_https_only_mode_send_http_background_request()) { return;
}
nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel(); if (!channel) { return;
}
// if neither HTTPS-Only nor HTTPS-First mode is enabled, then there is // nothing to do here. if ((!IsHttpsOnlyModeEnabled(isPrivateWin) &&
!IsHttpsFirstModeEnabled(isPrivateWin)) &&
!(loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
mozilla::StaticPrefs::dom_security_https_first_schemeless())) { return;
}
// if we are not dealing with a top-level load, then there is nothing to do // here. if (loadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_DOCUMENT) { return;
}
// if the load is exempt, then there is nothing to do here.
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); if (httpsOnlyStatus & nsILoadInfo::nsILoadInfo::HTTPS_ONLY_EXEMPT) { return;
}
// if it's not an http channel, then there is nothing to do here.
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); if (!httpChannel) { return;
}
// if it's not a GET method, then there is nothing to do here either.
nsAutoCString method;
mozilla::Unused << httpChannel->GetRequestMethod(method); if (!method.EqualsLiteral("GET")) { return;
}
// if it's already an https channel, then there is nothing to do here.
nsCOMPtr<nsIURI> channelURI;
channel->GetURI(getter_AddRefs(channelURI)); if (!channelURI->SchemeIs("http")) { return;
}
// Upgrades for custom ports may be disabled in that case // HTTPS-First only applies to standard ports but HTTPS-Only brute forces // all http connections to be https and overrules HTTPS-First. In case // HTTPS-First is enabled, but HTTPS-Only is not enabled, we might return // early if attempting to send a background request to a non standard port. if (!mozilla::StaticPrefs::dom_security_https_first_for_custom_ports() &&
(IsHttpsFirstModeEnabled(isPrivateWin) ||
(loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
mozilla::StaticPrefs::dom_security_https_first_schemeless()))) {
int32_t port = 0;
nsresult rv = channelURI->GetPort(&port); int defaultPortforScheme = NS_GetDefaultPort("http"); if (NS_SUCCEEDED(rv) && port != defaultPortforScheme && port != -1) { return;
}
}
// Check for general exceptions if (OnionException(channelURI) || LoopbackOrLocalException(channelURI)) { return;
}
RefPtr<nsIRunnable> task = new TestHTTPAnswerRunnable(channelURI, aDocumentLoadListener);
NS_DispatchToMainThread(task.forget());
}
/* static */ bool nsHTTPSOnlyUtils::ShouldUpgradeRequest(nsIURI* aURI,
nsILoadInfo* aLoadInfo) { // 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); if (!IsHttpsOnlyModeEnabled(isPrivateWin)) { returnfalse;
}
// 2. Check for general exceptions if (OnionException(aURI) || LoopbackOrLocalException(aURI)) { returnfalse;
}
// 3. Check if NoUpgrade-flag is set in LoadInfo
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
AutoTArray<nsString, 1> params = {
NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())};
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyNoUpgradeException", params,
nsIScriptError::infoFlag, aLoadInfo,
aURI); returnfalse;
}
// All subresources of an exempt triggering principal are also exempt
ExtContentPolicyType contentType = aLoadInfo->GetExternalContentPolicyType(); if (contentType != ExtContentPolicy::TYPE_DOCUMENT) { if (!aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
TestIfPrincipalIsExempt(aLoadInfo->TriggeringPrincipal())) { returnfalse;
}
}
// We can not upgrade "Save-As" downloads, since we have no way of detecting // if the upgrade failed (Bug 1674859). For now we will just allow the // download, since there will still be a visual warning about the download // being insecure. if (contentType == ExtContentPolicyType::TYPE_SAVEAS_DOWNLOAD) { returnfalse;
}
// We can upgrade the request - let's log it to the console // Appending an 's' to the scheme for the logging. (http -> https)
nsAutoCString scheme;
aURI->GetScheme(scheme);
scheme.AppendLiteral("s");
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
NS_ConvertUTF8toUTF16 reportScheme(scheme);
// If the status was not determined before, we now indicate that the request // will get upgraded, but no event-listener has been registered yet. if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) {
httpsOnlyStatus ^= nsILoadInfo::HTTPS_ONLY_UNINITIALIZED;
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED;
aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
} returntrue;
}
/* static */ bool nsHTTPSOnlyUtils::ShouldUpgradeWebSocket(nsIURI* aURI,
nsILoadInfo* aLoadInfo) { // 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); if (!IsHttpsOnlyModeEnabled(isPrivateWin)) { returnfalse;
}
// 2. Check for general exceptions if (OnionException(aURI) || LoopbackOrLocalException(aURI)) { returnfalse;
}
// 3. Check if NoUpgrade-flag is set in LoadInfo
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { // Let's log to the console, that we didn't upgrade this request
AutoTArray<nsString, 1> params = {
NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())};
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyNoUpgradeException", params,
nsIScriptError::infoFlag, aLoadInfo,
aURI); returnfalse;
}
// All subresources of an exempt triggering principal are also exempt. if (!aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
TestIfPrincipalIsExempt(aLoadInfo->TriggeringPrincipal())) { returnfalse;
}
// We can upgrade the request - let's log it to the console // Appending an 's' to the scheme for the logging. (ws -> wss)
nsAutoCString scheme;
aURI->GetScheme(scheme);
scheme.AppendLiteral("s");
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
NS_ConvertUTF8toUTF16 reportScheme(scheme);
/* static */ bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
nsIURI* aOldURI, nsIURI* aNewURI, nsILoadInfo* aLoadInfo, const mozilla::EnumSet<UpgradeDowngradeEndlessLoopOptions>& aOptions) { // 1. Check if the HTTPS-Only/HTTPS-First is even enabled, before doing // anything else bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); bool enforceForHTTPSOnlyMode =
IsHttpsOnlyModeEnabled(isPrivateWin) &&
aOptions.contains(
UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSOnlyMode); bool enforceForHTTPSFirstMode =
IsHttpsFirstModeEnabled(isPrivateWin) &&
aOptions.contains(
UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSFirstMode); bool enforceForHTTPSRR =
aOptions.contains(UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSRR); if (!enforceForHTTPSOnlyMode && !enforceForHTTPSFirstMode &&
!enforceForHTTPSRR) { returnfalse;
}
// 2. Check if the upgrade downgrade pref even wants us to try to break the // cycle. In the case that HTTPS RR is presented, we ignore this pref. if (!mozilla::StaticPrefs::
dom_security_https_only_mode_break_upgrade_downgrade_endless_loop() &&
!enforceForHTTPSRR) { returnfalse;
}
// 3. If it's not a top-level load, then there is nothing to do here either. if (aLoadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_DOCUMENT) { returnfalse;
}
// 4. If the load is exempt, then it's defintely not related to https-only
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); if ((httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) &&
!enforceForHTTPSRR) { returnfalse;
}
// 5. Check HTTP 3xx redirects. If the Principal that kicked off the // load/redirect is not https, then it's definitely not a redirect cause by // https-only. If the scheme of the principal however is https and the // asciiHost of the URI to be loaded and the asciiHost of the Principal are // identical, then we are dealing with an upgrade downgrade scenario and we // have to break the cycle. if (IsHttpDowngrade(aOldURI, aNewURI)) { returntrue;
}
// TODO(Bug 1896691): Don't depend on triggeringPrincipal for JS/Meta // redirects. Call this function at the correct places instead
// 6. Bug 1725026: Disable JS/Meta loop detection when the load was triggered // by a user gesture. This information is only when the redirect chain is // empty. When the redirect chain is not empty, this load is definitely // triggered by redirection, not a user gesture. // TODO(1896685): Verify whether check is still necessary. if (aLoadInfo->RedirectChain().IsEmpty()) { if (aLoadInfo->GetHasValidUserGestureActivation()) { returnfalse;
}
}
// 7. Meta redirects and JS based redirects (win.location). We detect them // during the https upgrade internal redirect.
nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal(); if (!triggeringPrincipal->SchemeIs("https")) { returnfalse;
}
// We detect Meta and JS based redirects during the upgrade. Check whether // we are currently in an upgrade situation here. if (!IsHttpDowngrade(aNewURI, aOldURI)) { returnfalse;
} // If we upgrade to the same URI that the load is origining from we are // creating a redirect loop. bool isLoop = false;
nsresult rv = triggeringPrincipal->EqualsURI(aNewURI, &isLoop);
NS_ENSURE_SUCCESS(rv, false); return isLoop;
}
/* static */ bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
nsILoadInfo* aLoadInfo) {
MOZ_ASSERT(aURI->SchemeIs("http"), "how come the request is not 'http'?");
// 1. Check if HTTPS-First Mode is enabled bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); if (!IsHttpsFirstModeEnabled(isPrivateWin) &&
!(aLoadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
mozilla::StaticPrefs::dom_security_https_first_schemeless())) { returnfalse;
} // 2. HTTPS-First only upgrades top-level loads (and speculative connections)
ExtContentPolicyType contentType = aLoadInfo->GetExternalContentPolicyType(); if (contentType != ExtContentPolicy::TYPE_DOCUMENT &&
contentType != ExtContentPolicy::TYPE_SPECULATIVE) { returnfalse;
}
// 3. Check for general exceptions if (OnionException(aURI) ||
(!mozilla::StaticPrefs::dom_security_https_first_for_local_addresses() &&
LoopbackOrLocalException(aURI)) ||
(!mozilla::StaticPrefs::dom_security_https_first_for_unknown_suffixes() &&
UnknownPublicSuffixException(aURI))) { returnfalse;
}
// 4. Don't upgrade if upgraded previously or exempt from upgrades
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { returnfalse;
}
// 5. Don't upgrade if the user explicitly provided a scheme if (aLoadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeful &&
aLoadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_SPECULATIVE &&
aURI->SchemeIs("http")) {
AddHTTPSFirstException(aURI, aLoadInfo); returnfalse;
}
// 6. Make sure HTTPS-First does not upgrade custom ports when it is disabled if (!mozilla::StaticPrefs::dom_security_https_first_for_custom_ports()) { int defaultPortforScheme = NS_GetDefaultPort("http"); // If no port is specified, then the API returns -1 to indicate the default // port.
int32_t port = 0;
nsresult rv = aURI->GetPort(&port);
NS_ENSURE_SUCCESS(rv, false); if (port != defaultPortforScheme && port != -1) { returnfalse;
}
}
// 7. Do not upgrade requests other than GET if (!aLoadInfo->GetIsGETRequest()) { returnfalse;
}
// We can upgrade the request - let's log to the console and set the status // so we know that we upgraded the request. if (aLoadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless &&
!IsHttpsFirstModeEnabled(isPrivateWin)) {
nsAutoCString urlCString;
aURI->GetSpec(urlCString);
NS_ConvertUTF8toUTF16 urlString(urlCString);
// Set flag so we know that we upgraded the request
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST;
aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); returntrue;
}
/* static */
already_AddRefed<nsIURI>
nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(
mozilla::net::DocumentLoadListener* aDocumentLoadListener,
nsresult aStatus) {
nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel();
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); // Only downgrade if we this request was upgraded using HTTPS-First Mode if (!(httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) { return nullptr;
} // Once loading is in progress we set that flag so that timeout counter // measures do not kick in.
loadInfo->SetHttpsOnlyStatus(
httpsOnlyStatus | nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS);
nsresult status = aStatus; // Since 4xx and 5xx errors return NS_OK instead of NS_ERROR_*, we need // to check each NS_OK for those errors. // Only downgrade an NS_OK status if it is an 4xx or 5xx error. if (NS_SUCCEEDED(aStatus)) {
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); // If no httpChannel exists we have nothing to do here. if (!httpChannel) { return nullptr;
}
uint32_t responseStatus = 0; if (NS_FAILED(httpChannel->GetResponseStatus(&responseStatus))) { return nullptr;
}
// In case we found one 4xx or 5xx error we need to log it later on, // for that reason we flip the nsresult 'status' from 'NS_OK' to the // corresponding NS_ERROR_*. // To do so we convert the response status to an nsresult error // Every NS_OK that is NOT an 4xx or 5xx error code won't get downgraded. if (responseStatus >= 400 && responseStatus < 600) { // HttpProxyResponseToErrorCode() maps 400 and 404 on // the same error as a 500 status which would lead to no downgrade // later on. For that reason we explicit filter for 400 and 404 status // codes to log them correctly and to downgrade them if possible. switch (responseStatus) { case 400:
status = NS_ERROR_PROXY_BAD_REQUEST; break; case 404:
status = NS_ERROR_PROXY_NOT_FOUND; break; default:
status = mozilla::net::HttpProxyResponseToErrorCode(responseStatus); break;
}
} if (NS_SUCCEEDED(status)) { return nullptr;
}
}
// We're only downgrading if it's possible that the error was // caused by the upgrade. if (HttpsUpgradeUnrelatedErrorCode(status)) { return nullptr;
}
// Only downgrade if the current scheme is (a) https or (b) view-source:https if (uri->SchemeIs("https")) {
rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, nullptr);
if (mozilla::StaticPrefs::
dom_security_https_first_add_exception_on_failure()) {
AddHTTPSFirstException(uri, loadInfo);
}
return newURI.forget();
}
void nsHTTPSOnlyUtils::UpdateLoadStateAfterHTTPSFirstDowngrade(
mozilla::net::DocumentLoadListener* aDocumentLoadListener,
nsDocShellLoadState* aLoadState) { // We have to exempt the load from HTTPS-First to prevent a upgrade-downgrade // loop
aLoadState->SetIsExemptFromHTTPSFirstMode(true);
// we can safely set the flag to indicate the downgrade here and it will be // propagated all the way to nsHttpChannel::OnStopRequest() where we collect // the telemetry.
nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel();
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); if (loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless) {
aLoadState->SetHttpsUpgradeTelemetry(
nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE_DOWNGRADE);
} else {
aLoadState->SetHttpsUpgradeTelemetry(
nsILoadInfo::HTTPS_FIRST_UPGRADE_DOWNGRADE);
}
// Add downgrade data for later telemetry usage to load state
nsDOMNavigationTiming* timing = aDocumentLoadListener->GetTiming(); if (timing) {
mozilla::TimeStamp navigationStart = timing->GetNavigationStartTimeStamp(); if (navigationStart) {
mozilla::TimeDuration duration =
mozilla::TimeStamp::Now() - navigationStart;
/* static */ bool nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(nsIChannel* aChannel,
nsresult aError) { // If there is no failed channel, then there is nothing to do here. if (!aChannel) { returnfalse;
}
// If HTTPS-Only Mode is not enabled, then there is nothing to do here.
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); bool isPrivateWin = loadInfo->GetOriginAttributes().IsPrivateBrowsing(); if (!IsHttpsOnlyModeEnabled(isPrivateWin)) { returnfalse;
}
// If the load is exempt or did not get upgraded, // then there is nothing to do here.
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT ||
httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) { returnfalse;
}
// If it's one of those errors, then most likely it's not a HTTPS-Only error // (This list of errors is largely drawn from nsDocShell::DisplayLoadError()) return !HttpsUpgradeUnrelatedErrorCode(aError);
}
// If HTTPS-Only or HTTPS-First Mode is not enabled, then there is nothing to // do here.
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); bool isPrivateWin = loadInfo->GetOriginAttributes().IsPrivateBrowsing(); bool isHttpsOnly = IsHttpsOnlyModeEnabled(isPrivateWin); bool isHttpsFirst = IsHttpsFirstModeEnabled(isPrivateWin); bool isSchemelessHttpsFirst =
(loadInfo->GetSchemelessInput() ==
nsILoadInfo::SchemelessInputTypeSchemeless) &&
mozilla::StaticPrefs::dom_security_https_first_schemeless() &&
!isHttpsOnly && !isHttpsFirst; if (!isHttpsOnly && !isHttpsFirst && !isSchemelessHttpsFirst) { return;
}
// if it's not a top-level load then there is nothing to here.
ExtContentPolicyType type = loadInfo->GetExternalContentPolicyType(); if (type != ExtContentPolicy::TYPE_DOCUMENT) { return;
}
// it it's not an http channel, then there is nothing to do here.
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); if (!httpChannel) { return;
}
// For the telemetry we do not want downgrade values to be overwritten // in the loadinfo. We only want e.g. a reload() or a back() click // to carry the upgrade exception. if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
nsILoadInfo::HTTPSUpgradeTelemetryType httpsTelemetry =
nsILoadInfo::NOT_INITIALIZED;
loadInfo->GetHttpsUpgradeTelemetry(&httpsTelemetry); if (httpsTelemetry != nsILoadInfo::HTTPS_ONLY_UPGRADE_DOWNGRADE &&
httpsTelemetry != nsILoadInfo::HTTPS_FIRST_UPGRADE_DOWNGRADE &&
httpsTelemetry !=
nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE_DOWNGRADE) {
loadInfo->SetHttpsUpgradeTelemetry(nsILoadInfo::UPGRADE_EXCEPTION);
}
}
}
/* static */ bool nsHTTPSOnlyUtils::IsSafeToAcceptCORSOrMixedContent(
nsILoadInfo* aLoadInfo) { // Check if the request is exempt from upgrades if ((aLoadInfo->GetHttpsOnlyStatus() & nsILoadInfo::HTTPS_ONLY_EXEMPT)) { returnfalse;
} // Check if HTTPS-Only Mode is enabled for this request bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); return nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin);
}
/* static */ void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags,
nsILoadInfo* aLoadInfo, nsIURI* aURI, bool aUseHttpsFirst) { // do not log to the console if the loadinfo says we should not!
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE) { return;
}
// Prepending HTTPS-Only to the outgoing console message
nsString message;
message.Append(aUseHttpsFirst ? u"HTTPS-First Mode: "_ns
: u"HTTPS-Only Mode: "_ns);
message.Append(aMessage);
// Allow for easy distinction in devtools code. auto category = aUseHttpsFirst ? "HTTPSFirst"_ns : "HTTPSOnly"_ns;
uint64_t windowId = aLoadInfo->GetInnerWindowID(); if (!windowId) {
windowId = aLoadInfo->GetTriggeringWindowId();
} if (windowId) { // Send to content console
nsContentUtils::ReportToConsoleByWindowID(
message, aFlags, category, windowId, mozilla::SourceLocation(aURI));
} else { // Send to browser console bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing();
nsContentUtils::LogSimpleConsoleError(message, category, isPrivateWin, true/* from chrome context */,
aFlags);
}
}
/* ------ Exceptions ------ */
/* static */ bool nsHTTPSOnlyUtils::OnionException(nsIURI* aURI) { // Onion-host exception can get disabled with a pref if (mozilla::StaticPrefs::dom_security_https_only_mode_upgrade_onion()) { returnfalse;
}
nsAutoCString host;
aURI->GetHost(host); return StringEndsWith(host, ".onion"_ns);
}
// Let's make a quick check if the host matches these loopback strings // before we do anything else if (asciiHost.EqualsLiteral("localhost") || asciiHost.EqualsLiteral("::1")) { returntrue;
}
mozilla::net::NetAddr addr; if (NS_FAILED(addr.InitFromString(asciiHost))) { returnfalse;
} // Loopback IPs are always exempt if (addr.IsLoopbackAddr()) { returntrue;
}
// Local IP exception can get disabled with a pref bool upgradeLocal =
mozilla::StaticPrefs::dom_security_https_only_mode_upgrade_local(); return (!upgradeLocal && addr.IsIPAddrLocal());
}
/* static */ bool nsHTTPSOnlyUtils::ShouldUpgradeConnection(nsILoadInfo* aLoadInfo) { // Check if one of parameters is null then webpage can't be loaded yet // and no further inspections are needed if (!aLoadInfo) { returnfalse;
}
// Check if the HTTPS-Only Mode is even enabled, before we do anything else bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); if (!IsHttpsOnlyModeEnabled(isPrivateWin) &&
!IsHttpsFirstModeEnabled(isPrivateWin)) { returnfalse;
}
// If the load is exempt, then don't upgrade
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { returnfalse;
} returntrue;
}
// 2. If the target URI is not http, then it's not a http downgrade if (!mozilla::net::SchemeIsHTTP(aToURI)) { returnfalse;
}
// 3. If the origin URI isn't https, then it's not a http downgrade either. if (!mozilla::net::SchemeIsHTTPS(aFromURI)) { returnfalse;
}
// 4. Create a new target URI with 'https' instead of 'http' and compare it // to the origin URI
int32_t port = 0;
nsresult rv = aToURI->GetPort(&port);
NS_ENSURE_SUCCESS(rv, false); // a port of -1 indicates the default port, hence we upgrade from port 80 to // port 443 // otherwise we keep the port. if (port == -1) {
port = NS_GetDefaultPort("https");
}
nsCOMPtr<nsIURI> newHTTPSchemeURI;
rv = NS_MutateURI(aToURI)
.SetScheme("https"_ns)
.SetPort(port)
.Finalize(newHTTPSchemeURI);
NS_ENSURE_SUCCESS(rv, false);
bool uriEquals = false; if (NS_FAILED(aFromURI->EqualsExceptRef(newHTTPSchemeURI, &uriEquals))) { returnfalse;
}
return uriEquals;
}
/* static */
nsresult nsHTTPSOnlyUtils::AddHTTPSFirstException(
nsCOMPtr<nsIURI> aURI, nsILoadInfo* const aLoadInfo) { // We need to reconstruct a principal instead of taking one from the loadinfo, // as the permission needs a http scheme, while the passed URL or principals // on the loadinfo may have a https scheme.
nsresult rv =
NS_MutateURI(aURI).SetScheme("http"_ns).Finalize(getter_AddRefs(aURI));
NS_ENSURE_SUCCESS(rv, rv);
mozilla::OriginAttributes oa = aLoadInfo->GetOriginAttributes();
oa.SetFirstPartyDomain(true, aURI);
/* static */ bool TestHTTPAnswerRunnable::IsBackgroundRequestRedirected(
nsIHttpChannel* aChannel) { // If there is no background request (aChannel), then there is nothing // to do here. if (!aChannel) { returnfalse;
} // If the request was not redirected, then there is nothing to do here.
nsCOMPtr<nsILoadInfo> loadinfo = aChannel->LoadInfo(); if (loadinfo->RedirectChain().IsEmpty()) { returnfalse;
}
// If the final URI is not targeting an https scheme, then we definitely not // dealing with a 'same-origin' redirect.
nsCOMPtr<nsIURI> finalURI;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
NS_ENSURE_SUCCESS(rv, false); if (!finalURI->SchemeIs("https")) { returnfalse;
}
// If the background request was not http, then there is nothing to do here.
nsCOMPtr<nsIPrincipal> firstURIPrincipal;
loadinfo->RedirectChain()[0]->GetPrincipal(getter_AddRefs(firstURIPrincipal)); if (!firstURIPrincipal || !firstURIPrincipal->SchemeIs("http")) { returnfalse;
}
// By now we have verified that the inital background request was http and // that the redirected scheme is https. We want to find the following case // where the background channel redirects to the https version of the // top-level request. // --> background channel: http://example.com // |--> redirects to: https://example.com // Now we have to check that the hosts are 'same-origin'.
nsAutoCString redirectHost;
nsAutoCString finalHost;
firstURIPrincipal->GetAsciiHost(redirectHost);
finalURI->GetAsciiHost(finalHost); return finalHost.Equals(redirectHost);
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::OnStartRequest(nsIRequest* aRequest) { // If the request status is not OK, it means it encountered some // kind of error in which case we do not want to do anything.
nsresult requestStatus;
aRequest->GetStatus(&requestStatus); if (requestStatus != NS_OK) { return NS_OK;
}
// Check if the original top-level channel which https-only is trying // to upgrade is already in progress or if the channel is an auth channel. // If it is in progress or Auth is in progress, then all good, if not // then let's cancel that channel so we can dispaly the exception page.
nsCOMPtr<nsIChannel> docChannel = mDocumentLoadListener->GetChannel();
nsCOMPtr<nsIHttpChannel> httpsOnlyChannel = do_QueryInterface(docChannel); if (httpsOnlyChannel) {
nsCOMPtr<nsILoadInfo> loadInfo = httpsOnlyChannel->LoadInfo();
uint32_t topLevelLoadInProgress =
loadInfo->GetHttpsOnlyStatus() &
nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS;
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
do_QueryInterface(httpsOnlyChannel); bool isAuthChannel = false;
mozilla::Unused << httpChannelInternal->GetIsAuthChannel(&isAuthChannel); // some server configurations need a long time to respond to an https // connection, but also redirect any http connection to the https version of // it. If the top-level load has not started yet, but the http background // request redirects to https, then do not show the error page, but keep // waiting for the https response of the upgraded top-level request. if (!topLevelLoadInProgress) {
nsCOMPtr<nsIHttpChannel> backgroundHttpChannel =
do_QueryInterface(aRequest);
topLevelLoadInProgress =
IsBackgroundRequestRedirected(backgroundHttpChannel);
} if (!topLevelLoadInProgress && !isAuthChannel) { // Only really cancel the original top-level channel if it's // status is still NS_OK, otherwise it might have already // encountered some other error and was cancelled.
nsresult httpsOnlyChannelStatus;
httpsOnlyChannel->GetStatus(&httpsOnlyChannelStatus); if (httpsOnlyChannelStatus == NS_OK) {
httpsOnlyChannel->Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
}
}
}
// Cancel this http request because it has reached the end of it's // lifetime at this point.
aRequest->Cancel(NS_ERROR_ABORT); return NS_ERROR_ABORT;
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::OnDataAvailable(nsIRequest* aRequest,
nsIInputStream* aStream,
uint64_t aOffset, uint32_t aCount) { // TestHTTPAnswerRunnable only cares about ::OnStartRequest which // will also cancel the request, so we should in fact never even // get here.
MOZ_ASSERT(false, "how come we get to ::OnDataAvailable"); return NS_OK;
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::OnStopRequest(nsIRequest* aRequest,
nsresult aStatusCode) { // TestHTTPAnswerRunnable only cares about ::OnStartRequest return NS_OK;
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::Run() { // Wait N milliseconds to give the original https request a heads start // before firing up this http request in the background. By default the // timer is set to 3 seconds. If the https request has not received // any signal from the server during that time, than it's almost // certain the upgraded request will result in time out.
uint32_t background_timer_ms = mozilla::StaticPrefs::
dom_security_https_only_fire_http_request_background_timer_ms();
// If the original channel has already started loading at this point // then there is no need to do the dance.
nsCOMPtr<nsIChannel> origChannel = mDocumentLoadListener->GetChannel();
nsCOMPtr<nsILoadInfo> origLoadInfo = origChannel->LoadInfo();
uint32_t origHttpsOnlyStatus = origLoadInfo->GetHttpsOnlyStatus();
uint32_t topLevelLoadInProgress =
origHttpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS;
uint32_t downloadInProgress =
origHttpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DOWNLOAD_IN_PROGRESS; // If the upgrade is caused by HSTS we do not allow downgrades so we do not // need to start a racing request. // TODO: We should do the same for HTTPS RR but it is more difficult // and the spec hasn't decided yet. // https://bugzilla.mozilla.org/show_bug.cgi?id=1906590 bool isClientRequestedUpgrade =
origHttpsOnlyStatus &
(nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED |
nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST);
if (topLevelLoadInProgress || downloadInProgress ||
!isClientRequestedUpgrade) { return NS_OK;
}
// No need to connect to the URI including the path because we only care about // the round trip time if a server responds to an http request.
nsCOMPtr<nsIURI> backgroundChannelURI;
nsAutoCString prePathStr;
nsresult rv = mURI->GetPrePath(prePathStr); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
rv = NS_NewURI(getter_AddRefs(backgroundChannelURI), prePathStr); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
// we are using TYPE_OTHER because TYPE_DOCUMENT might have side effects
nsCOMPtr<nsIChannel> testHTTPChannel;
rv = NS_NewChannel(getter_AddRefs(testHTTPChannel), backgroundChannelURI,
nullPrincipal,
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
nsIContentPolicy::TYPE_OTHER, nullptr, nullptr, nullptr,
nullptr, loadFlags);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
}
// We have exempt that load from HTTPS-Only to avoid getting upgraded // to https as well. Additonally let's not log that request to the console // because it might confuse end users.
nsCOMPtr<nsILoadInfo> loadInfo = testHTTPChannel->LoadInfo();
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT |
nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE |
nsILoadInfo::HTTPS_ONLY_BYPASS_ORB;
loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.