Bug 1658924: Implement HTTPS-First and automatically fall back to http if secure top-level connection is not available r=necko-reviewers,JulianWels,mattwoodrow,dragana
Differential Revision: https://phabricator.services.mozilla.com/D111686
This commit is contained in:
@@ -305,6 +305,10 @@ https://127.0.0.3:433 privileged,cer
|
|||||||
https://badcertdomain.example.com:82 privileged,cert=badCertDomain
|
https://badcertdomain.example.com:82 privileged,cert=badCertDomain
|
||||||
https://mismatch.badcertdomain.example.com:443 privileged,cert=badCertDomain
|
https://mismatch.badcertdomain.example.com:443 privileged,cert=badCertDomain
|
||||||
|
|
||||||
|
# Hosts for HTTPS-First upgrades/downgrades
|
||||||
|
http://httpsfirst.com:80 privileged
|
||||||
|
https://httpsfirst.com:443 privileged,nocert
|
||||||
|
|
||||||
# Hosts for sha1 console warning tests
|
# Hosts for sha1 console warning tests
|
||||||
https://sha1ee.example.com:443 privileged,cert=sha1_end_entity
|
https://sha1ee.example.com:443 privileged,cert=sha1_end_entity
|
||||||
https://sha256ee.example.com:443 privileged,cert=sha256_end_entity
|
https://sha256ee.example.com:443 privileged,cert=sha256_end_entity
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ nsDocShellLoadState::nsDocShellLoadState(
|
|||||||
mInheritPrincipal = aLoadState.InheritPrincipal();
|
mInheritPrincipal = aLoadState.InheritPrincipal();
|
||||||
mPrincipalIsExplicit = aLoadState.PrincipalIsExplicit();
|
mPrincipalIsExplicit = aLoadState.PrincipalIsExplicit();
|
||||||
mForceAllowDataURI = aLoadState.ForceAllowDataURI();
|
mForceAllowDataURI = aLoadState.ForceAllowDataURI();
|
||||||
|
mIsExemptFromHTTPSOnlyMode = aLoadState.IsExemptFromHTTPSOnlyMode();
|
||||||
mOriginalFrameSrc = aLoadState.OriginalFrameSrc();
|
mOriginalFrameSrc = aLoadState.OriginalFrameSrc();
|
||||||
mIsFormSubmission = aLoadState.IsFormSubmission();
|
mIsFormSubmission = aLoadState.IsFormSubmission();
|
||||||
mLoadType = aLoadState.LoadType();
|
mLoadType = aLoadState.LoadType();
|
||||||
@@ -103,6 +104,7 @@ nsDocShellLoadState::nsDocShellLoadState(const nsDocShellLoadState& aOther)
|
|||||||
mPrincipalToInherit(aOther.mPrincipalToInherit),
|
mPrincipalToInherit(aOther.mPrincipalToInherit),
|
||||||
mPartitionedPrincipalToInherit(aOther.mPartitionedPrincipalToInherit),
|
mPartitionedPrincipalToInherit(aOther.mPartitionedPrincipalToInherit),
|
||||||
mForceAllowDataURI(aOther.mForceAllowDataURI),
|
mForceAllowDataURI(aOther.mForceAllowDataURI),
|
||||||
|
mIsExemptFromHTTPSOnlyMode(aOther.mIsExemptFromHTTPSOnlyMode),
|
||||||
mOriginalFrameSrc(aOther.mOriginalFrameSrc),
|
mOriginalFrameSrc(aOther.mOriginalFrameSrc),
|
||||||
mIsFormSubmission(aOther.mIsFormSubmission),
|
mIsFormSubmission(aOther.mIsFormSubmission),
|
||||||
mLoadType(aOther.mLoadType),
|
mLoadType(aOther.mLoadType),
|
||||||
@@ -144,6 +146,7 @@ nsDocShellLoadState::nsDocShellLoadState(nsIURI* aURI, uint64_t aLoadIdentifier)
|
|||||||
mPrincipalIsExplicit(false),
|
mPrincipalIsExplicit(false),
|
||||||
mNotifiedBeforeUnloadListeners(false),
|
mNotifiedBeforeUnloadListeners(false),
|
||||||
mForceAllowDataURI(false),
|
mForceAllowDataURI(false),
|
||||||
|
mIsExemptFromHTTPSOnlyMode(false),
|
||||||
mOriginalFrameSrc(false),
|
mOriginalFrameSrc(false),
|
||||||
mIsFormSubmission(false),
|
mIsFormSubmission(false),
|
||||||
mLoadType(LOAD_NORMAL),
|
mLoadType(LOAD_NORMAL),
|
||||||
@@ -486,6 +489,15 @@ void nsDocShellLoadState::SetForceAllowDataURI(bool aForceAllowDataURI) {
|
|||||||
mForceAllowDataURI = aForceAllowDataURI;
|
mForceAllowDataURI = aForceAllowDataURI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool nsDocShellLoadState::IsExemptFromHTTPSOnlyMode() const {
|
||||||
|
return mIsExemptFromHTTPSOnlyMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nsDocShellLoadState::SetIsExemptFromHTTPSOnlyMode(
|
||||||
|
bool aIsExemptFromHTTPSOnlyMode) {
|
||||||
|
mIsExemptFromHTTPSOnlyMode = aIsExemptFromHTTPSOnlyMode;
|
||||||
|
}
|
||||||
|
|
||||||
bool nsDocShellLoadState::OriginalFrameSrc() const { return mOriginalFrameSrc; }
|
bool nsDocShellLoadState::OriginalFrameSrc() const { return mOriginalFrameSrc; }
|
||||||
|
|
||||||
void nsDocShellLoadState::SetOriginalFrameSrc(bool aOriginalFrameSrc) {
|
void nsDocShellLoadState::SetOriginalFrameSrc(bool aOriginalFrameSrc) {
|
||||||
@@ -933,6 +945,7 @@ DocShellLoadStateInit nsDocShellLoadState::Serialize() {
|
|||||||
loadState.InheritPrincipal() = mInheritPrincipal;
|
loadState.InheritPrincipal() = mInheritPrincipal;
|
||||||
loadState.PrincipalIsExplicit() = mPrincipalIsExplicit;
|
loadState.PrincipalIsExplicit() = mPrincipalIsExplicit;
|
||||||
loadState.ForceAllowDataURI() = mForceAllowDataURI;
|
loadState.ForceAllowDataURI() = mForceAllowDataURI;
|
||||||
|
loadState.IsExemptFromHTTPSOnlyMode() = mIsExemptFromHTTPSOnlyMode;
|
||||||
loadState.OriginalFrameSrc() = mOriginalFrameSrc;
|
loadState.OriginalFrameSrc() = mOriginalFrameSrc;
|
||||||
loadState.IsFormSubmission() = mIsFormSubmission;
|
loadState.IsFormSubmission() = mIsFormSubmission;
|
||||||
loadState.LoadType() = mLoadType;
|
loadState.LoadType() = mLoadType;
|
||||||
|
|||||||
@@ -131,6 +131,10 @@ class nsDocShellLoadState final {
|
|||||||
|
|
||||||
void SetForceAllowDataURI(bool aForceAllowDataURI);
|
void SetForceAllowDataURI(bool aForceAllowDataURI);
|
||||||
|
|
||||||
|
bool IsExemptFromHTTPSOnlyMode() const;
|
||||||
|
|
||||||
|
void SetIsExemptFromHTTPSOnlyMode(bool aIsExemptFromHTTPSOnlyMode);
|
||||||
|
|
||||||
bool OriginalFrameSrc() const;
|
bool OriginalFrameSrc() const;
|
||||||
|
|
||||||
void SetOriginalFrameSrc(bool aOriginalFrameSrc);
|
void SetOriginalFrameSrc(bool aOriginalFrameSrc);
|
||||||
@@ -404,6 +408,10 @@ class nsDocShellLoadState final {
|
|||||||
// to a data URI will be allowed.
|
// to a data URI will be allowed.
|
||||||
bool mForceAllowDataURI;
|
bool mForceAllowDataURI;
|
||||||
|
|
||||||
|
// If this attribute is true, then the top-level navigaion
|
||||||
|
// will be exempt from HTTPS-Only-Mode upgrades.
|
||||||
|
bool mIsExemptFromHTTPSOnlyMode;
|
||||||
|
|
||||||
// If this attribute is true, this load corresponds to a frame
|
// If this attribute is true, this load corresponds to a frame
|
||||||
// element loading its original src (or srcdoc) attribute.
|
// element loading its original src (or srcdoc) attribute.
|
||||||
bool mOriginalFrameSrc;
|
bool mOriginalFrameSrc;
|
||||||
|
|||||||
@@ -250,6 +250,7 @@ struct DocShellLoadStateInit
|
|||||||
nsIPrincipal PrincipalToInherit;
|
nsIPrincipal PrincipalToInherit;
|
||||||
nsIPrincipal PartitionedPrincipalToInherit;
|
nsIPrincipal PartitionedPrincipalToInherit;
|
||||||
bool ForceAllowDataURI;
|
bool ForceAllowDataURI;
|
||||||
|
bool IsExemptFromHTTPSOnlyMode;
|
||||||
bool OriginalFrameSrc;
|
bool OriginalFrameSrc;
|
||||||
bool IsFormSubmission;
|
bool IsFormSubmission;
|
||||||
uint32_t LoadType;
|
uint32_t LoadType;
|
||||||
|
|||||||
@@ -138,6 +138,8 @@ HTTPSOnlyUpgradeRequest = Upgrading insecure request “%1$S” to use “%2$S
|
|||||||
HTTPSOnlyNoUpgradeException = Not upgrading insecure request “%1$S” because it is exempt.
|
HTTPSOnlyNoUpgradeException = Not upgrading insecure request “%1$S” because it is exempt.
|
||||||
# LOCALIZATION NOTE: %1$S is the URL of the failed request; %2$S is an error-code.
|
# LOCALIZATION NOTE: %1$S is the URL of the failed request; %2$S is an error-code.
|
||||||
HTTPSOnlyFailedRequest = Upgrading insecure request “%1$S” failed. (%2$S)
|
HTTPSOnlyFailedRequest = Upgrading insecure request “%1$S” failed. (%2$S)
|
||||||
|
# LOCALIZATION NOTE: %S is the URL of the failed request;
|
||||||
|
HTTPSOnlyFailedDowngradeAgain = Upgrading insecure request “%S” failed. Downgrading to “http” again.
|
||||||
|
|
||||||
# LOCALIZATION NOTE: %S is the URL of the blocked request;
|
# LOCALIZATION NOTE: %S is the URL of the blocked request;
|
||||||
IframeSandboxBlockedDownload = Download of “%S” was blocked because the triggering iframe has the sandbox flag set.
|
IframeSandboxBlockedDownload = Download of “%S” was blocked because the triggering iframe has the sandbox flag set.
|
||||||
|
|||||||
@@ -44,6 +44,11 @@ bool nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(bool aFromPrivateWindow) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled() {
|
||||||
|
return mozilla::StaticPrefs::dom_security_https_only_mode_https_first();
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(
|
void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(
|
||||||
mozilla::net::DocumentLoadListener* aDocumentLoadListener) {
|
mozilla::net::DocumentLoadListener* aDocumentLoadListener) {
|
||||||
@@ -62,8 +67,9 @@ void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(
|
|||||||
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
||||||
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
||||||
|
|
||||||
// if https-only mode is not even enabled, then there is nothing to do here.
|
// if neither HTTPS-Only nor HTTPS-First mode is enabled, then there is
|
||||||
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
|
// nothing to do here.
|
||||||
|
if (!IsHttpsOnlyModeEnabled(isPrivateWin) && !IsHttpsFirstModeEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +219,7 @@ bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(nsIURI* aURI,
|
|||||||
nsILoadInfo* aLoadInfo) {
|
nsILoadInfo* aLoadInfo) {
|
||||||
// 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else
|
// 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else
|
||||||
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
||||||
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
|
if (!IsHttpsOnlyModeEnabled(isPrivateWin) && !IsHttpsFirstModeEnabled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +292,93 @@ bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(nsIURI* aURI,
|
|||||||
return uriHost.Equals(triggeringHost);
|
return uriHost.Equals(triggeringHost);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
|
||||||
|
nsILoadInfo* aLoadInfo) {
|
||||||
|
// 1. Check if HTTPS-First Mode is enabled
|
||||||
|
if (!IsHttpsFirstModeEnabled()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. HTTPS-First only upgrades top-level loads
|
||||||
|
if (aLoadInfo->GetExternalContentPolicyType() !=
|
||||||
|
ExtContentPolicy::TYPE_DOCUMENT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Don't upgrade if upgraded previously or exempt from upgrades
|
||||||
|
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
||||||
|
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST ||
|
||||||
|
httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can upgrade the request - let's log to the console and set the status
|
||||||
|
// so we know that we upgraded the request.
|
||||||
|
MOZ_ASSERT(aURI->SchemeIs("http"), "how come the request is not 'http'?");
|
||||||
|
nsAutoCString scheme;
|
||||||
|
aURI->GetScheme(scheme);
|
||||||
|
scheme.AppendLiteral("s");
|
||||||
|
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
|
||||||
|
NS_ConvertUTF8toUTF16 reportScheme(scheme);
|
||||||
|
|
||||||
|
AutoTArray<nsString, 2> params = {reportSpec, reportScheme};
|
||||||
|
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyUpgradeRequest", params,
|
||||||
|
nsIScriptError::warningFlag, aLoadInfo,
|
||||||
|
aURI, true);
|
||||||
|
|
||||||
|
// Set flag so we know that we upgraded the request
|
||||||
|
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST;
|
||||||
|
aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
already_AddRefed<nsIURI>
|
||||||
|
nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(nsIChannel* aChannel,
|
||||||
|
nsresult aError) {
|
||||||
|
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No matter if we're downgrading or not, the request failed so we need to
|
||||||
|
// inform the background request.
|
||||||
|
loadInfo->SetHttpsOnlyStatus(
|
||||||
|
httpsOnlyStatus | nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS);
|
||||||
|
|
||||||
|
// We're only downgrading if it's possible that the error was
|
||||||
|
// caused by the upgrade.
|
||||||
|
if (HttpsUpgradeUnrelatedErrorCode(aError)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
|
||||||
|
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||||
|
|
||||||
|
// Only downgrade if the current scheme is HTTPS
|
||||||
|
if (!uri->SchemeIs("https")) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the scheme to http
|
||||||
|
nsCOMPtr<nsIURI> newURI;
|
||||||
|
mozilla::Unused << NS_MutateURI(uri).SetScheme("http"_ns).Finalize(
|
||||||
|
getter_AddRefs(newURI));
|
||||||
|
|
||||||
|
// Log downgrade to console
|
||||||
|
NS_ConvertUTF8toUTF16 reportSpec(uri->GetSpecOrDefault());
|
||||||
|
AutoTArray<nsString, 1> params = {reportSpec};
|
||||||
|
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedDowngradeAgain", params,
|
||||||
|
nsIScriptError::warningFlag, loadInfo,
|
||||||
|
uri, true);
|
||||||
|
|
||||||
|
return newURI.forget();
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(nsIChannel* aChannel,
|
bool nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(nsIChannel* aChannel,
|
||||||
nsresult aError) {
|
nsresult aError) {
|
||||||
@@ -311,14 +404,7 @@ bool nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(nsIChannel* aChannel,
|
|||||||
|
|
||||||
// If it's one of those errors, then most likely it's not a HTTPS-Only error
|
// 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())
|
// (This list of errors is largely drawn from nsDocShell::DisplayLoadError())
|
||||||
return !(NS_ERROR_UNKNOWN_PROTOCOL == aError ||
|
return !HttpsUpgradeUnrelatedErrorCode(aError);
|
||||||
NS_ERROR_FILE_NOT_FOUND == aError ||
|
|
||||||
NS_ERROR_FILE_ACCESS_DENIED == aError ||
|
|
||||||
NS_ERROR_UNKNOWN_HOST == aError || NS_ERROR_PHISHING_URI == aError ||
|
|
||||||
NS_ERROR_MALWARE_URI == aError || NS_ERROR_UNWANTED_URI == aError ||
|
|
||||||
NS_ERROR_HARMFUL_URI == aError ||
|
|
||||||
NS_ERROR_CONTENT_CRASHED == aError ||
|
|
||||||
NS_ERROR_FRAME_CRASHED == aError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
@@ -391,23 +477,35 @@ bool nsHTTPSOnlyUtils::IsSafeToAcceptCORSOrMixedContent(
|
|||||||
return nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin);
|
return nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool nsHTTPSOnlyUtils::HttpsUpgradeUnrelatedErrorCode(nsresult aError) {
|
||||||
|
return NS_ERROR_UNKNOWN_PROTOCOL == aError ||
|
||||||
|
NS_ERROR_FILE_NOT_FOUND == aError ||
|
||||||
|
NS_ERROR_FILE_ACCESS_DENIED == aError ||
|
||||||
|
NS_ERROR_UNKNOWN_HOST == aError || NS_ERROR_PHISHING_URI == aError ||
|
||||||
|
NS_ERROR_MALWARE_URI == aError || NS_ERROR_UNWANTED_URI == aError ||
|
||||||
|
NS_ERROR_HARMFUL_URI == aError || NS_ERROR_CONTENT_CRASHED == aError ||
|
||||||
|
NS_ERROR_FRAME_CRASHED == aError;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------ Logging ------ */
|
/* ------ Logging ------ */
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void nsHTTPSOnlyUtils::LogLocalizedString(const char* aName,
|
void nsHTTPSOnlyUtils::LogLocalizedString(const char* aName,
|
||||||
const nsTArray<nsString>& aParams,
|
const nsTArray<nsString>& aParams,
|
||||||
uint32_t aFlags,
|
uint32_t aFlags,
|
||||||
nsILoadInfo* aLoadInfo,
|
nsILoadInfo* aLoadInfo, nsIURI* aURI,
|
||||||
nsIURI* aURI) {
|
bool aUseHttpsFirst) {
|
||||||
nsAutoString logMsg;
|
nsAutoString logMsg;
|
||||||
nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
|
nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
|
||||||
aName, aParams, logMsg);
|
aName, aParams, logMsg);
|
||||||
LogMessage(logMsg, aFlags, aLoadInfo, aURI);
|
LogMessage(logMsg, aFlags, aLoadInfo, aURI, aUseHttpsFirst);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags,
|
void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags,
|
||||||
nsILoadInfo* aLoadInfo, nsIURI* aURI) {
|
nsILoadInfo* aLoadInfo, nsIURI* aURI,
|
||||||
|
bool aUseHttpsFirst) {
|
||||||
// do not log to the console if the loadinfo says we should not!
|
// do not log to the console if the loadinfo says we should not!
|
||||||
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
||||||
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE) {
|
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE) {
|
||||||
@@ -416,11 +514,12 @@ void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags,
|
|||||||
|
|
||||||
// Prepending HTTPS-Only to the outgoing console message
|
// Prepending HTTPS-Only to the outgoing console message
|
||||||
nsString message;
|
nsString message;
|
||||||
message.AppendLiteral(u"HTTPS-Only Mode: ");
|
message.Append(aUseHttpsFirst ? u"HTTPS-First Mode: "_ns
|
||||||
|
: u"HTTPS-Only Mode: "_ns);
|
||||||
message.Append(aMessage);
|
message.Append(aMessage);
|
||||||
|
|
||||||
// Allow for easy distinction in devtools code.
|
// Allow for easy distinction in devtools code.
|
||||||
nsCString category("HTTPSOnly");
|
nsCString category(aUseHttpsFirst ? "HTTPSFirst" : "HTTPSOnly");
|
||||||
|
|
||||||
uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
|
uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
|
||||||
if (innerWindowId > 0) {
|
if (innerWindowId > 0) {
|
||||||
|
|||||||
@@ -14,12 +14,18 @@
|
|||||||
class nsHTTPSOnlyUtils {
|
class nsHTTPSOnlyUtils {
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Returns if HTTPSOnly-Mode preference is enabled
|
* Returns if HTTPS-Only Mode preference is enabled
|
||||||
* @param aFromPrivateWindow true if executing in private browsing mode
|
* @param aFromPrivateWindow true if executing in private browsing mode
|
||||||
* @return true if HTTPS-Only Mode is enabled
|
* @return true if HTTPS-Only Mode is enabled
|
||||||
*/
|
*/
|
||||||
static bool IsHttpsOnlyModeEnabled(bool aFromPrivateWindow);
|
static bool IsHttpsOnlyModeEnabled(bool aFromPrivateWindow);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns if HTTPS-First Mode preference is enabled
|
||||||
|
* @return true if HTTPS-First Mode is enabled
|
||||||
|
*/
|
||||||
|
static bool IsHttpsFirstModeEnabled();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Potentially fires an http request for a top-level load (provided by
|
* Potentially fires an http request for a top-level load (provided by
|
||||||
* aDocumentLoadListener) in the background to avoid long timeouts in case
|
* aDocumentLoadListener) in the background to avoid long timeouts in case
|
||||||
@@ -64,8 +70,29 @@ class nsHTTPSOnlyUtils {
|
|||||||
nsILoadInfo* aLoadInfo);
|
nsILoadInfo* aLoadInfo);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the error code is on a block-list of codes that are probably not
|
* Determines if a request should get upgraded because of the HTTPS-First
|
||||||
* related to a HTTPS-Only Mode upgrade.
|
* mode. If true, the httpsOnlyStatus in LoadInfo gets updated and a message
|
||||||
|
* is logged in the console.
|
||||||
|
* @param aURI nsIURI of request
|
||||||
|
* @param aLoadInfo nsILoadInfo of request
|
||||||
|
* @return true if request should get upgraded
|
||||||
|
*/
|
||||||
|
static bool ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
|
||||||
|
nsILoadInfo* aLoadInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the request was previously upgraded with HTTPS-First, creates
|
||||||
|
* a downgraded URI and logs to console.
|
||||||
|
* @param aError Error code
|
||||||
|
* @param aChannel Failed channel
|
||||||
|
* @return URI with http-scheme or nullptr
|
||||||
|
*/
|
||||||
|
static already_AddRefed<nsIURI> PotentiallyDowngradeHttpsFirstRequest(
|
||||||
|
nsIChannel* aChannel, nsresult aError);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the error code is on a block-list of codes that are probably
|
||||||
|
* not related to a HTTPS-Only Mode upgrade.
|
||||||
* @param aChannel The failed Channel.
|
* @param aChannel The failed Channel.
|
||||||
* @param aError Error Code from Request
|
* @param aError Error Code from Request
|
||||||
* @return false if error is not related to upgrade
|
* @return false if error is not related to upgrade
|
||||||
@@ -74,16 +101,18 @@ class nsHTTPSOnlyUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Logs localized message to either content console or browser console
|
* Logs localized message to either content console or browser console
|
||||||
* @param aName Localization key
|
* @param aName Localization key
|
||||||
* @param aParams Localization parameters
|
* @param aParams Localization parameters
|
||||||
* @param aFlags Logging Flag (see nsIScriptError)
|
* @param aFlags Logging Flag (see nsIScriptError)
|
||||||
* @param aLoadInfo The loadinfo of the request.
|
* @param aLoadInfo The loadinfo of the request.
|
||||||
* @param [aURI] Optional: URI to log
|
* @param [aURI] Optional: URI to log
|
||||||
|
* @param [aUseHttpsFirst] Optional: Log using HTTPS-First (vs. HTTPS-Only)
|
||||||
*/
|
*/
|
||||||
static void LogLocalizedString(const char* aName,
|
static void LogLocalizedString(const char* aName,
|
||||||
const nsTArray<nsString>& aParams,
|
const nsTArray<nsString>& aParams,
|
||||||
uint32_t aFlags, nsILoadInfo* aLoadInfo,
|
uint32_t aFlags, nsILoadInfo* aLoadInfo,
|
||||||
nsIURI* aURI = nullptr);
|
nsIURI* aURI = nullptr,
|
||||||
|
bool aUseHttpsFirst = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests if the HTTPS-Only upgrade exception is set for a given principal.
|
* Tests if the HTTPS-Only upgrade exception is set for a given principal.
|
||||||
@@ -123,15 +152,24 @@ class nsHTTPSOnlyUtils {
|
|||||||
nsILoadInfo* aLoadInfo);
|
nsILoadInfo* aLoadInfo);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* Checks if it can be ruled out that the error has something
|
||||||
|
* to do with an HTTPS upgrade.
|
||||||
|
* @param aError error code
|
||||||
|
* @return true if error is unrelated to the upgrade
|
||||||
|
*/
|
||||||
|
static bool HttpsUpgradeUnrelatedErrorCode(nsresult aError);
|
||||||
/**
|
/**
|
||||||
* Logs localized message to either content console or browser console
|
* Logs localized message to either content console or browser console
|
||||||
* @param aMessage Message to log
|
* @param aMessage Message to log
|
||||||
* @param aFlags Logging Flag (see nsIScriptError)
|
* @param aFlags Logging Flag (see nsIScriptError)
|
||||||
* @param aLoadInfo The loadinfo of the request.
|
* @param aLoadInfo The loadinfo of the request.
|
||||||
* @param [aURI] Optional: URI to log
|
* @param [aURI] Optional: URI to log
|
||||||
|
* @param [aUseHttpsFirst] Optional: Log using HTTPS-First (vs. HTTPS-Only)
|
||||||
*/
|
*/
|
||||||
static void LogMessage(const nsAString& aMessage, uint32_t aFlags,
|
static void LogMessage(const nsAString& aMessage, uint32_t aFlags,
|
||||||
nsILoadInfo* aLoadInfo, nsIURI* aURI = nullptr);
|
nsILoadInfo* aLoadInfo, nsIURI* aURI = nullptr,
|
||||||
|
bool aUseHttpsFirst = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the URI ends with .onion
|
* Checks whether the URI ends with .onion
|
||||||
|
|||||||
5
dom/security/test/https-first/.eslintrc.js
Normal file
5
dom/security/test/https-first/.eslintrc.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
extends: ["plugin:mozilla/browser-test"],
|
||||||
|
};
|
||||||
4
dom/security/test/https-first/browser.ini
Normal file
4
dom/security/test/https-first/browser.ini
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[browser_httpsfirst.js]
|
||||||
|
support-files =
|
||||||
|
file_httpsfirst_timeout_server.sjs
|
||||||
|
[browser_httpsfirst_console_logging.js]
|
||||||
63
dom/security/test/https-first/browser_httpsfirst.js
Normal file
63
dom/security/test/https-first/browser_httpsfirst.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const TEST_PATH_HTTP = getRootDirectory(gTestPath).replace(
|
||||||
|
"chrome://mochitests/content",
|
||||||
|
"http://example.com"
|
||||||
|
);
|
||||||
|
|
||||||
|
const TIMEOUT_PAGE_URI_HTTP =
|
||||||
|
TEST_PATH_HTTP + "file_httpsfirst_timeout_server.sjs";
|
||||||
|
|
||||||
|
async function runPrefTest(aURI, aDesc, aAssertURLStartsWith) {
|
||||||
|
await BrowserTestUtils.withNewTab("about:blank", async function(browser) {
|
||||||
|
const loaded = BrowserTestUtils.browserLoaded(browser, false, null, true);
|
||||||
|
BrowserTestUtils.loadURI(browser, aURI);
|
||||||
|
await loaded;
|
||||||
|
|
||||||
|
await ContentTask.spawn(browser, { aDesc, aAssertURLStartsWith }, function({
|
||||||
|
aDesc,
|
||||||
|
aAssertURLStartsWith,
|
||||||
|
}) {
|
||||||
|
ok(
|
||||||
|
content.document.location.href.startsWith(aAssertURLStartsWith),
|
||||||
|
aDesc
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await runPrefTest(
|
||||||
|
"http://example.com",
|
||||||
|
"HTTPS-First disabled; Should not upgrade",
|
||||||
|
"http://"
|
||||||
|
);
|
||||||
|
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [["dom.security.https_only_mode_https_first", true]],
|
||||||
|
});
|
||||||
|
|
||||||
|
await runPrefTest(
|
||||||
|
"http://example.com",
|
||||||
|
"Should upgrade upgradeable website",
|
||||||
|
"https://"
|
||||||
|
);
|
||||||
|
|
||||||
|
await runPrefTest(
|
||||||
|
"http://httpsfirst.com",
|
||||||
|
"Should downgrade after error.",
|
||||||
|
"http://"
|
||||||
|
);
|
||||||
|
|
||||||
|
await runPrefTest(
|
||||||
|
"http://domain.does.not.exist",
|
||||||
|
"Should not downgrade on dnsNotFound error.",
|
||||||
|
"https://"
|
||||||
|
);
|
||||||
|
|
||||||
|
await runPrefTest(
|
||||||
|
TIMEOUT_PAGE_URI_HTTP,
|
||||||
|
"Should downgrade after timeout.",
|
||||||
|
"http://"
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
// Bug 1658924 - HTTPS-First Mode - Tests for console logging
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1658924
|
||||||
|
// This test makes sure that the various console messages from the HTTPS-First
|
||||||
|
// mode get logged to the console.
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test Cases
|
||||||
|
// description: Description of what the subtests expects.
|
||||||
|
// expectLogLevel: Expected log-level of a message.
|
||||||
|
// expectIncludes: Expected substrings the message should contain.
|
||||||
|
let tests = [
|
||||||
|
{
|
||||||
|
description: "Top-Level upgrade should get logged",
|
||||||
|
expectLogLevel: Ci.nsIConsoleMessage.warn,
|
||||||
|
expectIncludes: ["Upgrading insecure request", "to use", "httpsfirst.com"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Top-Level upgrade failure should get logged",
|
||||||
|
expectLogLevel: Ci.nsIConsoleMessage.warn,
|
||||||
|
expectIncludes: [
|
||||||
|
"Upgrading insecure request",
|
||||||
|
"failed",
|
||||||
|
"httpsfirst.com",
|
||||||
|
"Downgrading to",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
// A longer timeout is necessary for this test than the plain mochitests
|
||||||
|
// due to opening a new tab with the web console.
|
||||||
|
requestLongerTimeout(4);
|
||||||
|
|
||||||
|
// Enable HTTPS-First Mode and register console-listener
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [["dom.security.https_only_mode_https_first", true]],
|
||||||
|
});
|
||||||
|
Services.console.registerListener(on_new_message);
|
||||||
|
// 1. Upgrade page to https://
|
||||||
|
await BrowserTestUtils.loadURI(
|
||||||
|
gBrowser.selectedBrowser,
|
||||||
|
"http://httpsfirst.com"
|
||||||
|
);
|
||||||
|
|
||||||
|
await BrowserTestUtils.waitForCondition(() => tests.length === 0);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
Services.console.unregisterListener(on_new_message);
|
||||||
|
});
|
||||||
|
|
||||||
|
function on_new_message(msgObj) {
|
||||||
|
const message = msgObj.message;
|
||||||
|
const logLevel = msgObj.logLevel;
|
||||||
|
|
||||||
|
if (message.includes("HTTPS-First Mode:")) {
|
||||||
|
for (let i = 0; i < tests.length; i++) {
|
||||||
|
const testCase = tests[i];
|
||||||
|
// Check if log-level matches
|
||||||
|
if (logLevel !== testCase.expectLogLevel) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Check if all substrings are included
|
||||||
|
if (testCase.expectIncludes.some(str => !message.includes(str))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ok(true, testCase.description);
|
||||||
|
tests.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
function handleRequest(request, response)
|
||||||
|
{
|
||||||
|
// avoid confusing cache behaviors
|
||||||
|
response.setHeader("Cache-Control", "no-cache", false);
|
||||||
|
|
||||||
|
if (request.scheme === "https") {
|
||||||
|
// Simulating a timeout by processing the https request
|
||||||
|
// async and *never* return anything!
|
||||||
|
response.processAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// we should never get here; just in case, return something unexpected
|
||||||
|
response.write("do'h");
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ BROWSER_CHROME_MANIFESTS += [
|
|||||||
"cors/browser.ini",
|
"cors/browser.ini",
|
||||||
"csp/browser.ini",
|
"csp/browser.ini",
|
||||||
"general/browser.ini",
|
"general/browser.ini",
|
||||||
|
"https-first/browser.ini",
|
||||||
"https-only/browser.ini",
|
"https-only/browser.ini",
|
||||||
"mixedcontentblocker/browser.ini",
|
"mixedcontentblocker/browser.ini",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2931,6 +2931,13 @@
|
|||||||
value: false
|
value: false
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
# If true, top-level request will get upgraded to HTTPS and
|
||||||
|
# downgraded again if the request failed.
|
||||||
|
- name: dom.security.https_only_mode_https_first
|
||||||
|
type: RelaxedAtomicBool
|
||||||
|
value: false
|
||||||
|
mirror: always
|
||||||
|
|
||||||
- name: dom.security.unexpected_system_load_telemetry_enabled
|
- name: dom.security.unexpected_system_load_telemetry_enabled
|
||||||
type: bool
|
type: bool
|
||||||
value: true
|
value: true
|
||||||
|
|||||||
@@ -470,6 +470,12 @@ interface nsILoadInfo : nsISupports
|
|||||||
*/
|
*/
|
||||||
const unsigned long HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE = (1 << 5);
|
const unsigned long HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE = (1 << 5);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This flag indicates that the request should not be logged to the
|
||||||
|
* console.
|
||||||
|
*/
|
||||||
|
const unsigned long HTTPS_ONLY_UPGRADED_HTTPS_FIRST = (1 << 6);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upgrade state of HTTPS-Only Mode. The flag HTTPS_ONLY_EXEMPT can get
|
* Upgrade state of HTTPS-Only Mode. The flag HTTPS_ONLY_EXEMPT can get
|
||||||
* set on requests that should be excempt from an upgrade.
|
* set on requests that should be excempt from an upgrade.
|
||||||
|
|||||||
@@ -2856,7 +2856,8 @@ nsresult NS_ShouldSecureUpgrade(
|
|||||||
!nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) {
|
!nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) {
|
||||||
if (aLoadInfo) {
|
if (aLoadInfo) {
|
||||||
// Check if the request can get upgraded with the HTTPS-Only mode
|
// Check if the request can get upgraded with the HTTPS-Only mode
|
||||||
if (nsHTTPSOnlyUtils::ShouldUpgradeRequest(aURI, aLoadInfo)) {
|
if (nsHTTPSOnlyUtils::ShouldUpgradeRequest(aURI, aLoadInfo) ||
|
||||||
|
nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(aURI, aLoadInfo)) {
|
||||||
aShouldUpgrade = true;
|
aShouldUpgrade = true;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,6 +140,12 @@ static auto CreateDocumentLoadInfo(CanonicalBrowsingContext* aBrowsingContext,
|
|||||||
attrs, securityFlags, sandboxFlags);
|
attrs, securityFlags, sandboxFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aLoadState->IsExemptFromHTTPSOnlyMode()) {
|
||||||
|
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
|
||||||
|
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT;
|
||||||
|
loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
|
||||||
|
}
|
||||||
|
|
||||||
loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
|
loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
|
||||||
loadInfo->SetHasValidUserGestureActivation(
|
loadInfo->SetHasValidUserGestureActivation(
|
||||||
aLoadState->HasValidUserGestureActivation());
|
aLoadState->HasValidUserGestureActivation());
|
||||||
@@ -2159,6 +2165,17 @@ bool DocumentLoadListener::MaybeHandleLoadErrorWithURIFixup(nsresult aStatus) {
|
|||||||
mLoadStateInternalLoadFlags &
|
mLoadStateInternalLoadFlags &
|
||||||
nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
|
nsDocShell::INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP,
|
||||||
bc->UsePrivateBrowsing(), true, getter_AddRefs(newPostData));
|
bc->UsePrivateBrowsing(), true, getter_AddRefs(newPostData));
|
||||||
|
|
||||||
|
// If the request failed, the above attempt to fix it failed but it
|
||||||
|
// was upgraded using HTTPS-First, then let's check if we can downgrade
|
||||||
|
// the scheme to HTTP again.
|
||||||
|
bool isHTTPSFirstFixup = false;
|
||||||
|
if (NS_FAILED(aStatus) && !newURI) {
|
||||||
|
newURI = nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(mChannel,
|
||||||
|
aStatus);
|
||||||
|
isHTTPSFirstFixup = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!newURI) {
|
if (!newURI) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2179,6 +2196,12 @@ bool DocumentLoadListener::MaybeHandleLoadErrorWithURIFixup(nsresult aStatus) {
|
|||||||
|
|
||||||
loadState->SetPostDataStream(newPostData);
|
loadState->SetPostDataStream(newPostData);
|
||||||
|
|
||||||
|
if (isHTTPSFirstFixup) {
|
||||||
|
// We have to exempt the load from HTTPS-First to prevent a
|
||||||
|
// upgrade-downgrade loop.
|
||||||
|
loadState->SetIsExemptFromHTTPSOnlyMode(true);
|
||||||
|
}
|
||||||
|
|
||||||
bc->LoadURI(loadState, false);
|
bc->LoadURI(loadState, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user