зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1246540 HSTS Priming Proof of Concept
HSTS priming changes the order of mixed-content blocking and HSTS upgrades, and adds a priming request to check if a mixed-content load is accesible over HTTPS and the server supports upgrading via the Strict-Transport-Security header. Every call site that uses AsyncOpen2 passes through the mixed-content blocker, and has a LoadInfo. If the mixed-content blocker marks the load as needing HSTS priming, nsHttpChannel will build and send an HSTS priming request on the same URI with the scheme upgraded to HTTPS. If the server allows the upgrade, then channel performs an internal redirect to the HTTPS URI, otherwise use the result of mixed-content blocker to allow or block the load. nsISiteSecurityService adds an optional boolean out parameter to determine if the HSTS state is already cached for negative assertions. If the host has been probed within the previous 24 hours, no HSTS priming check will be sent. (r=ckerschb,r=mayhemer,r=jld,r=smaug,r=dkeeler,r=jmaher,p=ally)
This commit is contained in:
Родитель
a7c8429fc4
Коммит
c6650db185
|
@ -4966,10 +4966,10 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
|
|||
do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
|
||||
flags, &isStsHost);
|
||||
flags, nullptr, &isStsHost);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
|
||||
flags, &isPinnedHost);
|
||||
flags, nullptr, &isPinnedHost);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
mozilla::dom::ContentChild* cc =
|
||||
|
@ -9848,6 +9848,25 @@ nsDocShell::InternalLoad(nsIURI* aURI,
|
|||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
// If HSTS priming was set by nsMixedContentBlocker::ShouldLoad, and we
|
||||
// would block due to mixed content, go ahead and block here. If we try to
|
||||
// proceed with priming, we will error out later on.
|
||||
nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(context);
|
||||
NS_ENSURE_TRUE(docShell, NS_OK);
|
||||
if (docShell) {
|
||||
nsIDocument* document = docShell->GetDocument();
|
||||
NS_ENSURE_TRUE(document, NS_OK);
|
||||
|
||||
HSTSPrimingState state = document->GetHSTSPrimingStateForLocation(aURI);
|
||||
if (state == HSTSPrimingState::eHSTS_PRIMING_BLOCK) {
|
||||
// HSTS Priming currently disabled for InternalLoad, so we need to clear
|
||||
// the location that was added by nsMixedContentBlocker::ShouldLoad
|
||||
// Bug 1269815 will address images loaded via InternalLoad
|
||||
document->ClearHSTSPrimingLocation(aURI);
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> owner(aOwner);
|
||||
//
|
||||
// Get an owner from the current document if necessary. Note that we only
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsPIDOMWindow.h" // for use in inline functions
|
||||
#include "nsPropertyTable.h" // for member
|
||||
#include "nsTHashtable.h" // for member
|
||||
#include "nsDataHashtable.h" // for member
|
||||
#include "nsURIHashKey.h" // for member
|
||||
#include "mozilla/net/ReferrerPolicy.h" // for member
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/dom/DocumentBinding.h"
|
||||
|
@ -166,6 +167,13 @@ enum DocumentFlavor {
|
|||
DocumentFlavorPlain, // Just a Document
|
||||
};
|
||||
|
||||
// Enum for HSTS priming states
|
||||
enum class HSTSPrimingState {
|
||||
eNO_HSTS_PRIMING = 0, // don't do HSTS Priming
|
||||
eHSTS_PRIMING_ALLOW = 1, // if HSTS priming fails, allow the load to proceed
|
||||
eHSTS_PRIMING_BLOCK = 2 // if HSTS priming fails, block the load
|
||||
};
|
||||
|
||||
// Document states
|
||||
|
||||
// RTL locale: specific to the XUL localedir attribute
|
||||
|
@ -349,6 +357,34 @@ public:
|
|||
mReferrer = aReferrer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a subresource we want to load requires HSTS priming
|
||||
* to be done.
|
||||
*/
|
||||
HSTSPrimingState GetHSTSPrimingStateForLocation(nsIURI* aContentLocation) const
|
||||
{
|
||||
HSTSPrimingState state;
|
||||
if (mHSTSPrimingURIList.Get(aContentLocation, &state)) {
|
||||
return state;
|
||||
}
|
||||
return HSTSPrimingState::eNO_HSTS_PRIMING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a subresource to the HSTS priming list. If this URI is
|
||||
* not in the HSTS cache, it will trigger an HSTS priming request
|
||||
* when we try to load it.
|
||||
*/
|
||||
void AddHSTSPrimingLocation(nsIURI* aContentLocation, HSTSPrimingState aState)
|
||||
{
|
||||
mHSTSPrimingURIList.Put(aContentLocation, aState);
|
||||
}
|
||||
|
||||
void ClearHSTSPrimingLocation(nsIURI* aContentLocation)
|
||||
{
|
||||
mHSTSPrimingURIList.Remove(aContentLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the principal responsible for this document.
|
||||
*/
|
||||
|
@ -2869,6 +2905,11 @@ protected:
|
|||
bool mUpgradeInsecureRequests;
|
||||
bool mUpgradeInsecurePreloads;
|
||||
|
||||
// if nsMixedContentBlocker requires sending an HSTS priming request,
|
||||
// temporarily store that in the document so that it can be propogated to the
|
||||
// LoadInfo and eventually the HTTP Channel
|
||||
nsDataHashtable<nsURIHashKey, HSTSPrimingState> mHSTSPrimingURIList;
|
||||
|
||||
mozilla::WeakPtr<nsDocShell> mDocumentContainer;
|
||||
|
||||
nsCString mCharacterSet;
|
||||
|
|
|
@ -4289,18 +4289,18 @@ ContentParent::RecvIsSecureURI(const uint32_t& type,
|
|||
if (!ourURI) {
|
||||
return false;
|
||||
}
|
||||
nsresult rv = sss->IsSecureURI(type, ourURI, flags, isSecureURI);
|
||||
nsresult rv = sss->IsSecureURI(type, ourURI, flags, isSecureURI, nullptr);
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive)
|
||||
ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive, const bool& aHSTSPriming)
|
||||
{
|
||||
nsCOMPtr<nsIURI> ourURI = DeserializeURI(aURI);
|
||||
if (!ourURI) {
|
||||
return false;
|
||||
}
|
||||
nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive);
|
||||
nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aHSTSPriming);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -822,7 +822,8 @@ private:
|
|||
const uint32_t& aFlags, bool* aIsSecureURI) override;
|
||||
|
||||
virtual bool RecvAccumulateMixedContentHSTS(const URIParams& aURI,
|
||||
const bool& aActive) override;
|
||||
const bool& aActive,
|
||||
const bool& aHSTSPriming) override;
|
||||
|
||||
virtual bool DeallocPHalParent(PHalParent*) override;
|
||||
|
||||
|
|
|
@ -821,7 +821,7 @@ parent:
|
|||
sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
|
||||
returns (bool isSecureURI);
|
||||
|
||||
async AccumulateMixedContentHSTS(URIParams uri, bool active);
|
||||
async AccumulateMixedContentHSTS(URIParams uri, bool active, bool hasHSTSPriming);
|
||||
|
||||
sync GetLookAndFeelCache()
|
||||
returns (LookAndFeelInt[] lookAndFeelIntCache);
|
||||
|
|
|
@ -20,6 +20,7 @@ EXPORTS.mozilla.dom += [
|
|||
|
||||
EXPORTS += [
|
||||
'nsContentSecurityManager.h',
|
||||
'nsMixedContentBlocker.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsCORSListenerProxy.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsMixedContentBlocker.h"
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
|
@ -381,6 +383,14 @@ DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
|
|||
if (NS_CP_REJECTED(shouldLoad)) {
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
if (nsMixedContentBlocker::sSendHSTSPriming) {
|
||||
rv = nsMixedContentBlocker::MarkLoadInfoForPriming(uri,
|
||||
requestingContext,
|
||||
aLoadInfo);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -491,7 +501,7 @@ nsContentSecurityManager::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
|
|||
rv = nsContentUtils::GetSecurityManager()->
|
||||
CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aCb->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
|
|
|
@ -54,6 +54,11 @@ bool nsMixedContentBlocker::sBlockMixedScript = false;
|
|||
// Is mixed display content blocking (images, audio, video, <a ping>) enabled?
|
||||
bool nsMixedContentBlocker::sBlockMixedDisplay = false;
|
||||
|
||||
// Do we move HSTS before mixed-content
|
||||
bool nsMixedContentBlocker::sUseHSTS = false;
|
||||
// Do we send an HSTS priming request
|
||||
bool nsMixedContentBlocker::sSendHSTSPriming = false;
|
||||
|
||||
// Fired at the document that attempted to load mixed content. The UI could
|
||||
// handle this event, for example, by displaying an info bar that offers the
|
||||
// choice to reload the page with mixed content permitted.
|
||||
|
@ -195,6 +200,14 @@ nsMixedContentBlocker::nsMixedContentBlocker()
|
|||
// Cache the pref for mixed display blocking
|
||||
Preferences::AddBoolVarCache(&sBlockMixedDisplay,
|
||||
"security.mixed_content.block_display_content");
|
||||
|
||||
// Cache the pref for HSTS
|
||||
Preferences::AddBoolVarCache(&sUseHSTS,
|
||||
"security.mixed_content.use_hsts");
|
||||
|
||||
// Cache the pref for sending HSTS priming
|
||||
Preferences::AddBoolVarCache(&sSendHSTSPriming,
|
||||
"security.mixed_content.send_hsts_priming");
|
||||
}
|
||||
|
||||
nsMixedContentBlocker::~nsMixedContentBlocker()
|
||||
|
@ -241,8 +254,6 @@ LogMixedContentMessage(MixedContentTypes aClassification,
|
|||
messageLookupKey.get(), strings, ArrayLength(strings));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* nsIChannelEventSink implementation
|
||||
* This code is called when a request is redirected.
|
||||
* We check the channel associated with the new uri is allowed to load
|
||||
|
@ -466,7 +477,6 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
*aDecision = ACCEPT;
|
||||
return NS_OK;
|
||||
|
||||
|
||||
// Static display content is considered moderate risk for mixed content so
|
||||
// these will be blocked according to the mixed display preference
|
||||
case TYPE_IMAGE:
|
||||
|
@ -500,7 +510,6 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
// This content policy works as a whitelist.
|
||||
default:
|
||||
MOZ_ASSERT(false, "Mixed content of unknown type");
|
||||
break;
|
||||
}
|
||||
|
||||
// Make sure to get the URI the load started with. No need to check
|
||||
|
@ -680,7 +689,9 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
bool isHttpScheme = false;
|
||||
rv = innerContentLocation->SchemeIs("http", &isHttpScheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (isHttpScheme && docShell->GetDocument()->GetUpgradeInsecureRequests(isPreload)) {
|
||||
nsIDocument* document = docShell->GetDocument();
|
||||
MOZ_ASSERT(document, "Expected a document");
|
||||
if (isHttpScheme && document->GetUpgradeInsecureRequests(isPreload)) {
|
||||
*aDecision = ACCEPT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -691,7 +702,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
// Block all non secure loads in case the CSP directive is present. Please note
|
||||
// that at this point we already know, based on |schemeSecure| that the load is
|
||||
// not secure, so we can bail out early at this point.
|
||||
if (docShell->GetDocument()->GetBlockAllMixedContent(isPreload)) {
|
||||
if (document->GetBlockAllMixedContent(isPreload)) {
|
||||
// log a message to the console before returning.
|
||||
nsAutoCString spec;
|
||||
rv = aContentLocation->GetSpec(spec);
|
||||
|
@ -706,7 +717,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
0, // aLineNumber
|
||||
0, // aColumnNumber
|
||||
nsIScriptError::errorFlag, "CSP",
|
||||
docShell->GetDocument()->InnerWindowID());
|
||||
document->InnerWindowID());
|
||||
*aDecision = REJECT_REQUEST;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -799,6 +810,34 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
}
|
||||
nsresult stateRV = securityUI->GetState(&state);
|
||||
|
||||
bool doHSTSPriming = false;
|
||||
if (isHttpScheme) {
|
||||
bool hsts = false;
|
||||
bool cached = false;
|
||||
nsCOMPtr<nsISiteSecurityService> sss =
|
||||
do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aContentLocation,
|
||||
0, &cached, &hsts);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (hsts && sUseHSTS) {
|
||||
// assume we will be upgraded later
|
||||
*aDecision = ACCEPT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Send a priming request if the result is not already cached and priming
|
||||
// requests are allowed
|
||||
if (!cached && sSendHSTSPriming) {
|
||||
// add this URI as a priming location
|
||||
doHSTSPriming = true;
|
||||
document->AddHSTSPrimingLocation(innerContentLocation,
|
||||
HSTSPrimingState::eHSTS_PRIMING_ALLOW);
|
||||
*aDecision = ACCEPT;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we know that the request is mixed content, and the only
|
||||
// question is whether we block it. Record telemetry at this point as to
|
||||
// whether HSTS would have fixed things by making the content location
|
||||
|
@ -814,14 +853,14 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
bool active = (classification == eMixedScript);
|
||||
if (!aHadInsecureImageRedirect) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
AccumulateMixedContentHSTS(innerContentLocation, active);
|
||||
AccumulateMixedContentHSTS(innerContentLocation, active, doHSTSPriming);
|
||||
} else {
|
||||
// Ask the parent process to do the same call
|
||||
mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
|
||||
if (cc) {
|
||||
mozilla::ipc::URIParams uri;
|
||||
SerializeURI(innerContentLocation, uri);
|
||||
cc->SendAccumulateMixedContentHSTS(uri, active);
|
||||
cc->SendAccumulateMixedContentHSTS(uri, active, doHSTSPriming);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -864,7 +903,13 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
*aDecision = nsIContentPolicy::REJECT_REQUEST;
|
||||
if (doHSTSPriming) {
|
||||
document->AddHSTSPrimingLocation(innerContentLocation,
|
||||
HSTSPrimingState::eHSTS_PRIMING_BLOCK);
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
} else {
|
||||
*aDecision = nsIContentPolicy::REJECT_REQUEST;
|
||||
}
|
||||
LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
|
||||
if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
|
||||
rootDoc->SetHasMixedDisplayContentBlocked(true);
|
||||
|
@ -910,7 +955,13 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
}
|
||||
} else {
|
||||
//User has not overriden the pref by Disabling protection. Reject the request and update the security state.
|
||||
*aDecision = nsIContentPolicy::REJECT_REQUEST;
|
||||
if (doHSTSPriming) {
|
||||
document->AddHSTSPrimingLocation(innerContentLocation,
|
||||
HSTSPrimingState::eHSTS_PRIMING_BLOCK);
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
} else {
|
||||
*aDecision = nsIContentPolicy::REJECT_REQUEST;
|
||||
}
|
||||
LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
|
||||
// See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
|
||||
if (rootDoc->GetHasMixedActiveContentBlocked()) {
|
||||
|
@ -925,7 +976,6 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
|||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} else {
|
||||
// The content is not blocked by the mixed content prefs.
|
||||
|
||||
|
@ -976,10 +1026,24 @@ enum MixedContentHSTSState {
|
|||
MCB_HSTS_ACTIVE_WITH_HSTS = 3
|
||||
};
|
||||
|
||||
// Similar to the existing mixed-content HSTS, except MCB_HSTS_*_NO_HSTS is
|
||||
// broken into two distinct states, indicating whether we plan to send a priming
|
||||
// request or not. If we decided not go send a priming request, it could be
|
||||
// because it is a type we do not support, or because we cached a previous
|
||||
// negative response.
|
||||
enum MixedContentHSTSPrimingState {
|
||||
eMCB_HSTS_PASSIVE_WITH_HSTS = 0,
|
||||
eMCB_HSTS_ACTIVE_WITH_HSTS = 1,
|
||||
eMCB_HSTS_PASSIVE_NO_PRIMING = 2,
|
||||
eMCB_HSTS_PASSIVE_DO_PRIMING = 3,
|
||||
eMCB_HSTS_ACTIVE_NO_PRIMING = 4,
|
||||
eMCB_HSTS_ACTIVE_DO_PRIMING = 5
|
||||
};
|
||||
|
||||
// Record information on when HSTS would have made mixed content not mixed
|
||||
// content (regardless of whether it was actually blocked)
|
||||
void
|
||||
nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive)
|
||||
nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive, bool aHasHSTSPriming)
|
||||
{
|
||||
// This method must only be called in the parent, because
|
||||
// nsSiteSecurityService is only available in the parent
|
||||
|
@ -994,28 +1058,88 @@ nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive)
|
|||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, 0, &hsts);
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, 0, nullptr, &hsts);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// states: would upgrade, would prime, hsts info cached
|
||||
// active, passive
|
||||
//
|
||||
if (!aActive) {
|
||||
if (!hsts) {
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
|
||||
MCB_HSTS_PASSIVE_NO_HSTS);
|
||||
if (aHasHSTSPriming) {
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
|
||||
eMCB_HSTS_PASSIVE_DO_PRIMING);
|
||||
} else {
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
|
||||
eMCB_HSTS_PASSIVE_NO_PRIMING);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
|
||||
MCB_HSTS_PASSIVE_WITH_HSTS);
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
|
||||
eMCB_HSTS_PASSIVE_WITH_HSTS);
|
||||
}
|
||||
} else {
|
||||
if (!hsts) {
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
|
||||
MCB_HSTS_ACTIVE_NO_HSTS);
|
||||
if (aHasHSTSPriming) {
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
|
||||
eMCB_HSTS_ACTIVE_DO_PRIMING);
|
||||
} else {
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
|
||||
eMCB_HSTS_ACTIVE_NO_PRIMING);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
|
||||
MCB_HSTS_ACTIVE_WITH_HSTS);
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
|
||||
eMCB_HSTS_ACTIVE_WITH_HSTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
nsresult
|
||||
nsMixedContentBlocker::MarkLoadInfoForPriming(nsIURI* aURI,
|
||||
nsISupports* aRequestingContext,
|
||||
nsILoadInfo* aLoadInfo)
|
||||
{
|
||||
// If we marked for priming, we used the innermost URI, so get that
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
|
||||
if (!innerURI) {
|
||||
NS_ERROR("Can't get innerURI from aContentLocation");
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
bool isHttp = false;
|
||||
innerURI->SchemeIs("http", &isHttp);
|
||||
if (!isHttp) {
|
||||
// there is nothign to do
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If the DocShell was marked for HSTS priming, propagate that to the LoadInfo
|
||||
nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
|
||||
if (!docShell) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsIDocument* document = docShell->GetDocument();
|
||||
if (!document) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
HSTSPrimingState status = document->GetHSTSPrimingStateForLocation(innerURI);
|
||||
// set it on the loadInfo
|
||||
if (status != HSTSPrimingState::eNO_HSTS_PRIMING) {
|
||||
aLoadInfo->SetHSTSPriming(status == HSTSPrimingState::eHSTS_PRIMING_BLOCK);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ enum MixedContentTypes {
|
|||
#include "nsIChannelEventSink.h"
|
||||
#include "imgRequest.h"
|
||||
|
||||
class nsILoadInfo; // forward declaration
|
||||
|
||||
class nsMixedContentBlocker : public nsIContentPolicy,
|
||||
public nsIChannelEventSink
|
||||
{
|
||||
|
@ -59,9 +61,25 @@ public:
|
|||
nsISupports* aExtra,
|
||||
nsIPrincipal* aRequestPrincipal,
|
||||
int16_t* aDecision);
|
||||
static void AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive);
|
||||
static void AccumulateMixedContentHSTS(nsIURI* aURI,
|
||||
bool aActive,
|
||||
bool aHasHSTSPriming);
|
||||
/* If the document associated with aRequestingContext requires priming for
|
||||
* aURI, propagate that to the LoadInfo so the HttpChannel will find out about
|
||||
* it.
|
||||
*
|
||||
* @param aURI The URI associated with the load
|
||||
* @param aRequestingContext the requesting context passed to ShouldLoad
|
||||
* @param aLoadInfo the LoadInfo for the load
|
||||
*/
|
||||
static nsresult MarkLoadInfoForPriming(nsIURI* aURI,
|
||||
nsISupports* aRequestingContext,
|
||||
nsILoadInfo* aLoadInfo);
|
||||
|
||||
static bool sBlockMixedScript;
|
||||
static bool sBlockMixedDisplay;
|
||||
static bool sUseHSTS;
|
||||
static bool sSendHSTSPriming;
|
||||
};
|
||||
|
||||
#endif /* nsMixedContentBlocker_h___ */
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_priming-top.html
|
||||
file_testserver.sjs
|
||||
file_1x1.png
|
||||
file_priming.js
|
||||
file_stylesheet.css
|
||||
|
||||
[browser_hsts-priming_main.js]
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Description of the test:
|
||||
* Check that HSTS priming occurs correctly with mixed content
|
||||
*
|
||||
* This test uses three hostnames, each of which treats an HSTS priming
|
||||
* request differently.
|
||||
* * no-ssl never returns an ssl response
|
||||
* * reject-upgrade returns an ssl response, but with no STS header
|
||||
* * prime-hsts returns an ssl response with the appropriate STS header
|
||||
*
|
||||
* For each server, test that it response appropriately when the we allow
|
||||
* or block active or display content, as well as when we send an hsts priming
|
||||
* request, but do not change the order of mixed-content and HSTS.
|
||||
*
|
||||
* This test uses http-on-examine-response, so must be run in browser context.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var TOP_URI = "https://example.com/browser/dom/security/test/hsts/file_priming-top.html";
|
||||
|
||||
var test_servers = {
|
||||
// a test server that does not support TLS
|
||||
'no-ssl': {
|
||||
host: 'example.co.jp',
|
||||
response: false,
|
||||
id: 'no-ssl',
|
||||
},
|
||||
// a test server which does not support STS upgrade
|
||||
'reject-upgrade': {
|
||||
host: 'example.org',
|
||||
response: true,
|
||||
id: 'reject-upgrade',
|
||||
},
|
||||
// a test server when sends an STS header when priming
|
||||
'prime-hsts': {
|
||||
host: 'test1.example.com',
|
||||
response: true,
|
||||
id: 'prime-hsts'
|
||||
},
|
||||
};
|
||||
// The number of priming responses we expect to see
|
||||
var priming_count = 2;
|
||||
|
||||
var test_settings = {
|
||||
// mixed active content is allowed, priming will upgrade
|
||||
allow_active: {
|
||||
block_active: false,
|
||||
block_display: false,
|
||||
use_hsts: true,
|
||||
send_hsts_priming: true,
|
||||
type: 'script',
|
||||
result: {
|
||||
'no-ssl': 'insecure',
|
||||
'reject-upgrade': 'insecure',
|
||||
'prime-hsts': 'secure',
|
||||
},
|
||||
},
|
||||
// mixed active content is blocked, priming will upgrade
|
||||
block_active: {
|
||||
block_active: true,
|
||||
block_display: false,
|
||||
use_hsts: true,
|
||||
send_hsts_priming: true,
|
||||
type: 'script',
|
||||
result: {
|
||||
'no-ssl': 'blocked',
|
||||
'reject-upgrade': 'blocked',
|
||||
'prime-hsts': 'secure',
|
||||
},
|
||||
},
|
||||
// keep the original order of mixed-content and HSTS, but send
|
||||
// priming requests
|
||||
hsts_after_mixed: {
|
||||
block_active: true,
|
||||
block_display: false,
|
||||
use_hsts: false,
|
||||
send_hsts_priming: true,
|
||||
type: 'script',
|
||||
result: {
|
||||
'no-ssl': 'blocked',
|
||||
'reject-upgrade': 'blocked',
|
||||
'prime-hsts': 'blocked',
|
||||
},
|
||||
},
|
||||
// mixed display content is allowed, priming will upgrade
|
||||
allow_display: {
|
||||
block_active: true,
|
||||
block_display: false,
|
||||
use_hsts: true,
|
||||
send_hsts_priming: true,
|
||||
type: 'img',
|
||||
result: {
|
||||
'no-ssl': 'insecure',
|
||||
'reject-upgrade': 'insecure',
|
||||
'prime-hsts': 'secure',
|
||||
},
|
||||
},
|
||||
// mixed display content is blocked, priming will upgrade
|
||||
block_display: {
|
||||
block_active: true,
|
||||
block_display: true,
|
||||
use_hsts: true,
|
||||
send_hsts_priming: true,
|
||||
type: 'img',
|
||||
result: {
|
||||
'no-ssl': 'blocked',
|
||||
'reject-upgrade': 'blocked',
|
||||
'prime-hsts': 'secure',
|
||||
},
|
||||
},
|
||||
// mixed active content is blocked, priming will upgrade (css)
|
||||
block_active_css: {
|
||||
block_active: true,
|
||||
block_display: false,
|
||||
use_hsts: true,
|
||||
send_hsts_priming: true,
|
||||
type: 'css',
|
||||
result: {
|
||||
'no-ssl': 'blocked',
|
||||
'reject-upgrade': 'blocked',
|
||||
'prime-hsts': 'secure',
|
||||
},
|
||||
},
|
||||
// mixed active content is blocked, priming will upgrade
|
||||
// redirect to the same host
|
||||
block_active_with_redir_same: {
|
||||
block_active: true,
|
||||
block_display: false,
|
||||
use_hsts: true,
|
||||
send_hsts_priming: true,
|
||||
type: 'script',
|
||||
redir: 'same',
|
||||
result: {
|
||||
'no-ssl': 'blocked',
|
||||
'reject-upgrade': 'blocked',
|
||||
'prime-hsts': 'secure',
|
||||
},
|
||||
},
|
||||
}
|
||||
// track which test we are on
|
||||
var which_test = "";
|
||||
|
||||
const Observer = {
|
||||
observe: function (subject, topic, data) {
|
||||
switch (topic) {
|
||||
case 'console-api-log-event':
|
||||
return Observer.console_api_log_event(subject, topic, data);
|
||||
case 'http-on-examine-response':
|
||||
return Observer.http_on_examine_response(subject, topic, data);
|
||||
}
|
||||
throw "Can't handle topic "+topic;
|
||||
},
|
||||
// When a load is blocked which results in an error event within a page, the
|
||||
// test logs to the console.
|
||||
console_api_log_event: function (subject, topic, data) {
|
||||
var message = subject.wrappedJSObject.arguments[0];
|
||||
// when we are blocked, this will match the message we sent to the console,
|
||||
// ignore everything else.
|
||||
var re = RegExp(/^HSTS_PRIMING: Blocked ([-\w]+).*$/);
|
||||
if (!re.test(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let id = message.replace(re, '$1');
|
||||
let curTest =test_servers[id];
|
||||
|
||||
if (!curTest) {
|
||||
ok(false, "HSTS priming got a console message blocked, "+
|
||||
"but doesn't match expectations "+id+" (msg="+message);
|
||||
return;
|
||||
}
|
||||
|
||||
is("blocked", test_settings[which_test].result[curTest.id], "HSTS priming "+
|
||||
which_test+":"+curTest.id+" expected "+
|
||||
test_settings[which_test].result[curTest.id]+", got blocked");
|
||||
test_settings[which_test].finished[curTest.id] = "blocked";
|
||||
},
|
||||
// When we see a response come back, peek at the response and test it is secure
|
||||
// or insecure as needed. Addtionally, watch the response for priming requests.
|
||||
http_on_examine_response: function (subject, topic, data) {
|
||||
let curTest = null;
|
||||
let channel = subject.QueryInterface(Ci.nsIHttpChannel);
|
||||
for (let item in test_servers) {
|
||||
let re = RegExp('https?://'+test_servers[item].host);
|
||||
if (re.test(channel.URI.asciiSpec)) {
|
||||
curTest = test_servers[item];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!curTest) {
|
||||
return;
|
||||
}
|
||||
|
||||
let result = (channel.URI.asciiSpec.startsWith('https:')) ? "secure" : "insecure";
|
||||
|
||||
// This is a priming request, go ahead and validate we were supposed to see
|
||||
// a response from the server
|
||||
if (channel.requestMethod == 'HEAD') {
|
||||
is(true, curTest.response, "HSTS priming response found " + curTest.id);
|
||||
test_settings[which_test].priming[curTest.id] = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// This is the response to our query, make sure it matches
|
||||
is(result, test_settings[which_test].result[curTest.id],
|
||||
"HSTS priming result " + which_test + ":" + curTest.id);
|
||||
test_settings[which_test].finished[curTest.id] = result;
|
||||
},
|
||||
};
|
||||
|
||||
// opens `uri' in a new tab and focuses it.
|
||||
// returns the newly opened tab
|
||||
function openTab(uri) {
|
||||
let tab = gBrowser.addTab(uri);
|
||||
|
||||
// select tab and make sure its browser is focused
|
||||
gBrowser.selectedTab = tab;
|
||||
tab.ownerDocument.defaultView.focus();
|
||||
|
||||
return tab;
|
||||
}
|
||||
|
||||
function clear_sts_data() {
|
||||
for (let test in test_servers) {
|
||||
SpecialPowers.cleanUpSTSData('http://'+test_servers[test].host);
|
||||
}
|
||||
}
|
||||
|
||||
function do_cleanup() {
|
||||
clear_sts_data();
|
||||
|
||||
Services.obs.removeObserver(Observer, "console-api-log-event");
|
||||
Services.obs.removeObserver(Observer, "http-on-examine-response");
|
||||
}
|
||||
|
||||
function SetupPrefTestEnvironment(which) {
|
||||
which_test = which;
|
||||
clear_sts_data();
|
||||
|
||||
var settings = test_settings[which];
|
||||
// priming counts how many priming requests we saw
|
||||
settings.priming = {};
|
||||
// priming counts how many tests were finished
|
||||
settings.finished= {};
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.block_active_content",
|
||||
settings.block_active],
|
||||
["security.mixed_content.block_display_content",
|
||||
settings.block_display],
|
||||
["security.mixed_content.use_hsts",
|
||||
settings.use_hsts],
|
||||
["security.mixed_content.send_hsts_priming",
|
||||
settings.send_hsts_priming]]});
|
||||
}
|
||||
|
||||
// make the top-level test uri
|
||||
function build_test_uri(base_uri, host, test_id, type) {
|
||||
return base_uri +
|
||||
"?host=" + escape(host) +
|
||||
"&id=" + escape(test_id) +
|
||||
"&type=" + escape(type);
|
||||
}
|
||||
|
||||
// open a new tab, load the test, and wait for it to finish
|
||||
function execute_test(test, mimetype) {
|
||||
var src = build_test_uri(TOP_URI, test_servers[test].host,
|
||||
test, test_settings[which_test].type);
|
||||
|
||||
let tab = openTab(src);
|
||||
test_servers[test]['tab'] = tab;
|
||||
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
}
|
||||
|
||||
//jscs:disable
|
||||
add_task(function*() {
|
||||
//jscs:enable
|
||||
Services.obs.addObserver(Observer, "console-api-log-event", false);
|
||||
Services.obs.addObserver(Observer, "http-on-examine-response", false);
|
||||
registerCleanupFunction(do_cleanup);
|
||||
|
||||
for (let which of Object.keys(test_settings)) {
|
||||
SetupPrefTestEnvironment(which);
|
||||
|
||||
for (let server of Object.keys(test_servers)) {
|
||||
yield execute_test(server, test_settings[which].mimetype);
|
||||
}
|
||||
|
||||
SpecialPowers.popPrefEnv();
|
||||
}
|
||||
});
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 17 KiB |
|
@ -0,0 +1,84 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1246540</title>
|
||||
<meta http-equiv='content-type' content="text/html;charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="visibility: hidden">
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
/*
|
||||
* Description of the test:
|
||||
* Attempt to load an insecure resource. If the resource responds to HSTS
|
||||
* priming with an STS header, the load should continue securely.
|
||||
* If it does not, the load should continue be blocked or continue insecurely.
|
||||
*/
|
||||
|
||||
function parse_query_string() {
|
||||
var q = {};
|
||||
document.location.search.substr(1).
|
||||
split('&').forEach(function (item, idx, ar) {
|
||||
let [k, v] = item.split('=');
|
||||
q[k] = unescape(v);
|
||||
});
|
||||
return q;
|
||||
}
|
||||
|
||||
var args = parse_query_string();
|
||||
|
||||
var subresources = {
|
||||
css: { mimetype: 'text/css', file: 'file_stylesheet.css' },
|
||||
img: { mimetype: 'image/png', file: 'file_1x1.png' },
|
||||
script: { mimetype: 'text/javascript', file: 'file_priming.js' },
|
||||
};
|
||||
|
||||
function handler(ev) {
|
||||
console.log("HSTS_PRIMING: Blocked "+args.id);
|
||||
}
|
||||
|
||||
function loadCss(src) {
|
||||
let head = document.getElementsByTagName("head")[0];
|
||||
let link = document.createElement("link");
|
||||
link.setAttribute("rel", "stylesheet");
|
||||
link.setAttribute("type", subresources[args.type].mimetype);
|
||||
link.setAttribute("href", src);
|
||||
head.appendChild(link);
|
||||
}
|
||||
|
||||
function loadResource(src) {
|
||||
let content = document.getElementById("content");
|
||||
let testElem = document.createElement(args.type);
|
||||
testElem.setAttribute("id", args.id);
|
||||
testElem.setAttribute("charset", "UTF-8");
|
||||
testElem.onerror = handler;
|
||||
content.appendChild(testElem);
|
||||
testElem.src = src;
|
||||
}
|
||||
|
||||
function loadTest() {
|
||||
let subresource = subresources[args.type];
|
||||
|
||||
let src = "http://"
|
||||
+ args.host
|
||||
+ "/browser/dom/security/test/hsts/file_testserver.sjs"
|
||||
+ "?file=" +escape("browser/dom/security/test/hsts/" + subresource.file)
|
||||
+ "&primer=" + escape(args.id)
|
||||
+ "&mimetype=" + escape(subresource.mimetype)
|
||||
;
|
||||
if (args.type == 'css') {
|
||||
loadCss(src);
|
||||
return;
|
||||
}
|
||||
|
||||
loadResource(src);
|
||||
}
|
||||
|
||||
// start running the tests
|
||||
loadTest();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
function completed() {
|
||||
return;
|
||||
}
|
||||
completed();
|
|
@ -0,0 +1,66 @@
|
|||
// SJS file for HSTS mochitests
|
||||
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
Components.utils.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
function loadFromFile(path) {
|
||||
// Load the HTML to return in the response from file.
|
||||
// Since it's relative to the cwd of the test runner, we start there and
|
||||
// append to get to the actual path of the file.
|
||||
var testFile =
|
||||
Components.classes["@mozilla.org/file/directory_service;1"].
|
||||
getService(Components.interfaces.nsIProperties).
|
||||
get("CurWorkD", Components.interfaces.nsILocalFile);
|
||||
var dirs = path.split("/");
|
||||
for (var i = 0; i < dirs.length; i++) {
|
||||
testFile.append(dirs[i]);
|
||||
}
|
||||
var testFileStream =
|
||||
Components.classes["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Components.interfaces.nsIFileInputStream);
|
||||
testFileStream.init(testFile, -1, 0, 0);
|
||||
var test = NetUtil.readInputStreamToString(testFileStream, testFileStream.available());
|
||||
return test;
|
||||
}
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
const query = new URLSearchParams(request.queryString);
|
||||
|
||||
redir = query.get('redir');
|
||||
if (redir == 'same') {
|
||||
query.delete("redir");
|
||||
response.setStatus(302);
|
||||
let newURI = request.uri;
|
||||
newURI.queryString = query.serialize();
|
||||
response.setHeader("Location", newURI.spec)
|
||||
}
|
||||
|
||||
// avoid confusing cache behaviors
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
|
||||
// if we have a priming header, check for required behavior
|
||||
// and set header appropriately
|
||||
if (request.hasHeader('Upgrade-Insecure-Requests')) {
|
||||
var expected = query.get('primer');
|
||||
if (expected == 'prime-hsts') {
|
||||
// set it for 5 minutes
|
||||
response.setHeader("Strict-Transport-Security", "max-age="+(60*5), false);
|
||||
} else if (expected == 'reject-upgrade') {
|
||||
response.setHeader("Strict-Transport-Security", "max-age=0", false);
|
||||
}
|
||||
response.write('');
|
||||
return;
|
||||
}
|
||||
|
||||
var file = query.get('file');
|
||||
if (file) {
|
||||
var mimetype = unescape(query.get('mimetype'));
|
||||
response.setHeader("Content-Type", mimetype, false);
|
||||
response.write(loadFromFile(unescape(file)));
|
||||
return;
|
||||
}
|
||||
|
||||
response.setHeader("Content-Type", "application/json", false);
|
||||
response.write('{}');
|
||||
}
|
|
@ -162,6 +162,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=62178
|
|||
}
|
||||
|
||||
function startTest() {
|
||||
// Set prefs to use mixed-content before HSTS
|
||||
SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.use_hsts", false],
|
||||
["security.mixed_content.send_hsts_priming", false]]});
|
||||
//Set the first set of mixed content settings and increment the counter.
|
||||
//Enable <picture> and <img srcset> for the test.
|
||||
changePrefs([[ "dom.image.srcset.enabled", true ], [ "dom.image.picture.enabled", true ]],
|
||||
|
|
|
@ -27,4 +27,5 @@ MOCHITEST_CHROME_MANIFESTS += [
|
|||
BROWSER_CHROME_MANIFESTS += [
|
||||
'contentverifier/browser.ini',
|
||||
'csp/browser.ini',
|
||||
'hsts/browser.ini',
|
||||
]
|
||||
|
|
|
@ -263,7 +263,9 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
|
|||
redirectChain,
|
||||
aLoadInfo->CorsUnsafeHeaders(),
|
||||
aLoadInfo->GetForcePreflight(),
|
||||
aLoadInfo->GetIsPreflight());
|
||||
aLoadInfo->GetIsPreflight(),
|
||||
aLoadInfo->GetForceHSTSPriming(),
|
||||
aLoadInfo->GetMixedContentWouldBlock());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -329,7 +331,10 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
|
|||
redirectChain,
|
||||
loadInfoArgs.corsUnsafeHeaders(),
|
||||
loadInfoArgs.forcePreflight(),
|
||||
loadInfoArgs.isPreflight());
|
||||
loadInfoArgs.isPreflight(),
|
||||
loadInfoArgs.forceHSTSPriming(),
|
||||
loadInfoArgs.mixedContentWouldBlock()
|
||||
);
|
||||
|
||||
loadInfo.forget(outLoadInfo);
|
||||
return NS_OK;
|
||||
|
|
|
@ -60,6 +60,8 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
|
|||
, mIsThirdPartyContext(false)
|
||||
, mForcePreflight(false)
|
||||
, mIsPreflight(false)
|
||||
, mForceHSTSPriming(false)
|
||||
, mMixedContentWouldBlock(false)
|
||||
{
|
||||
MOZ_ASSERT(mLoadingPrincipal);
|
||||
MOZ_ASSERT(mTriggeringPrincipal);
|
||||
|
@ -213,6 +215,8 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
|
|||
, mIsThirdPartyContext(false) // NB: TYPE_DOCUMENT implies not third-party.
|
||||
, mForcePreflight(false)
|
||||
, mIsPreflight(false)
|
||||
, mForceHSTSPriming(false)
|
||||
, mMixedContentWouldBlock(false)
|
||||
{
|
||||
// Top-level loads are never third-party
|
||||
// Grab the information we can out of the window.
|
||||
|
@ -265,6 +269,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
|
|||
, mCorsUnsafeHeaders(rhs.mCorsUnsafeHeaders)
|
||||
, mForcePreflight(rhs.mForcePreflight)
|
||||
, mIsPreflight(rhs.mIsPreflight)
|
||||
, mForceHSTSPriming(rhs.mForceHSTSPriming)
|
||||
, mMixedContentWouldBlock(rhs.mMixedContentWouldBlock)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -288,7 +294,9 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
|
|||
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain,
|
||||
const nsTArray<nsCString>& aCorsUnsafeHeaders,
|
||||
bool aForcePreflight,
|
||||
bool aIsPreflight)
|
||||
bool aIsPreflight,
|
||||
bool aForceHSTSPriming,
|
||||
bool aMixedContentWouldBlock)
|
||||
: mLoadingPrincipal(aLoadingPrincipal)
|
||||
, mTriggeringPrincipal(aTriggeringPrincipal)
|
||||
, mSecurityFlags(aSecurityFlags)
|
||||
|
@ -308,6 +316,8 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
|
|||
, mCorsUnsafeHeaders(aCorsUnsafeHeaders)
|
||||
, mForcePreflight(aForcePreflight)
|
||||
, mIsPreflight(aIsPreflight)
|
||||
, mForceHSTSPriming (aForceHSTSPriming)
|
||||
, mMixedContentWouldBlock(aMixedContentWouldBlock)
|
||||
{
|
||||
// Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
|
||||
MOZ_ASSERT(mLoadingPrincipal || aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
|
||||
|
@ -760,6 +770,34 @@ LoadInfo::GetIsPreflight(bool* aIsPreflight)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadInfo::GetForceHSTSPriming(bool* aForceHSTSPriming)
|
||||
{
|
||||
*aForceHSTSPriming = mForceHSTSPriming;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadInfo::GetMixedContentWouldBlock(bool *aMixedContentWouldBlock)
|
||||
{
|
||||
*aMixedContentWouldBlock = mMixedContentWouldBlock;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
LoadInfo::SetHSTSPriming(bool aMixedContentWouldBlock)
|
||||
{
|
||||
mForceHSTSPriming = true;
|
||||
mMixedContentWouldBlock = aMixedContentWouldBlock;
|
||||
}
|
||||
|
||||
void
|
||||
LoadInfo::ClearHSTSPriming()
|
||||
{
|
||||
mForceHSTSPriming = false;
|
||||
mMixedContentWouldBlock = false;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadInfo::GetTainting(uint32_t* aTaintingOut)
|
||||
{
|
||||
|
|
|
@ -104,7 +104,9 @@ private:
|
|||
nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain,
|
||||
const nsTArray<nsCString>& aUnsafeHeaders,
|
||||
bool aForcePreflight,
|
||||
bool aIsPreflight);
|
||||
bool aIsPreflight,
|
||||
bool aForceHSTSPriming,
|
||||
bool aMixedContentWouldBlock);
|
||||
LoadInfo(const LoadInfo& rhs);
|
||||
|
||||
friend nsresult
|
||||
|
@ -145,6 +147,9 @@ private:
|
|||
nsTArray<nsCString> mCorsUnsafeHeaders;
|
||||
bool mForcePreflight;
|
||||
bool mIsPreflight;
|
||||
|
||||
bool mForceHSTSPriming : 1;
|
||||
bool mMixedContentWouldBlock : 1;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -541,6 +541,32 @@ interface nsILoadInfo : nsISupports
|
|||
*/
|
||||
[infallible] readonly attribute boolean isPreflight;
|
||||
|
||||
/**
|
||||
* When this request would be mixed-content and we do not have an
|
||||
* entry in the HSTS cache, we send an HSTS priming request to
|
||||
* determine if it is ok to upgrade the request to HTTPS.
|
||||
*/
|
||||
/**
|
||||
* True if this is a mixed-content load and HSTS priming request will be sent.
|
||||
*/
|
||||
[noscript, infallible] readonly attribute boolean forceHSTSPriming;
|
||||
/**
|
||||
* Carry the decision whether this load would be blocked by mixed content so
|
||||
* that if HSTS priming fails, the correct decision can be made.
|
||||
*/
|
||||
[noscript, infallible] readonly attribute boolean mixedContentWouldBlock;
|
||||
|
||||
/**
|
||||
* Mark this LoadInfo as needing HSTS Priming
|
||||
*
|
||||
* @param wouldBlock Carry the decision of Mixed Content Blocking to be
|
||||
* applied when HSTS priming is complete.
|
||||
*/
|
||||
[noscript, notxpcom, nostdcall]
|
||||
void setHSTSPriming(in boolean mixeContentWouldBlock);
|
||||
[noscript, notxpcom, nostdcall]
|
||||
void clearHSTSPriming();
|
||||
|
||||
/**
|
||||
* Constants reflecting the channel tainting. These are mainly defined here
|
||||
* for script. Internal C++ code should use the enum defined in LoadTainting.h.
|
||||
|
|
|
@ -2339,7 +2339,7 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
|
|||
bool isStsHost = false;
|
||||
uint32_t flags = aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
|
||||
&isStsHost);
|
||||
nullptr, &isStsHost);
|
||||
|
||||
// if the SSS check fails, it's likely because this load is on a
|
||||
// malformed URI or something else in the setup is wrong, so any error
|
||||
|
|
|
@ -97,3 +97,15 @@ pref("security.ssl.errorReporting.automatic", false);
|
|||
// blacking themselves out by setting a bad pin. (60 days by default)
|
||||
// https://tools.ietf.org/html/rfc7469#section-4.1
|
||||
pref("security.cert_pinning.max_max_age_seconds", 5184000);
|
||||
|
||||
// If a request is mixed-content, send an HSTS priming request to attempt to
|
||||
// see if it is available over HTTPS.
|
||||
pref("security.mixed_content.send_hsts_priming", true);
|
||||
#ifdef RELEASE_BUILD
|
||||
// Don't change the order of evaluation of mixed-content and HSTS upgrades
|
||||
pref("security.mixed_content.use_hsts", false);
|
||||
#else
|
||||
// Change the order of evaluation so HSTS upgrades happen before
|
||||
// mixed-content blocking
|
||||
pref("security.mixed_content.use_hsts", true);
|
||||
#endif
|
||||
|
|
|
@ -49,6 +49,8 @@ struct LoadInfoArgs
|
|||
nsCString[] corsUnsafeHeaders;
|
||||
bool forcePreflight;
|
||||
bool isPreflight;
|
||||
bool forceHSTSPriming;
|
||||
bool mixedContentWouldBlock;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "nsHttp.h"
|
||||
|
||||
#include "HSTSPrimerListener.h"
|
||||
#include "nsIHstsPrimingCallback.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsSecurityHeaderParser.h"
|
||||
#include "nsISiteSecurityService.h"
|
||||
#include "nsISocketProvider.h"
|
||||
#include "nsISSLStatus.h"
|
||||
#include "nsISSLStatusProvider.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "LoadInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
NS_IMPL_ISUPPORTS(HSTSPrimingListener, nsIStreamListener,
|
||||
nsIRequestObserver, nsIInterfaceRequestor)
|
||||
|
||||
NS_IMETHODIMP
|
||||
HSTSPrimingListener::GetInterface(const nsIID & aIID, void **aResult)
|
||||
{
|
||||
return QueryInterface(aIID, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HSTSPrimingListener::OnStartRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext)
|
||||
{
|
||||
nsresult rv = CheckHSTSPrimingRequestStatus(aRequest);
|
||||
nsCOMPtr<nsIHstsPrimingCallback> callback(mCallback);
|
||||
mCallback = nullptr;
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("HSTS Priming Failed (request was not approved)"));
|
||||
return callback->OnHSTSPrimingFailed(rv, false);
|
||||
}
|
||||
|
||||
LOG(("HSTS Priming Succeeded (request was approved)"));
|
||||
return callback->OnHSTSPrimingSucceeded(false);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HSTSPrimingListener::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatus)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HSTSPrimingListener::CheckHSTSPrimingRequestStatus(nsIRequest* aRequest)
|
||||
{
|
||||
nsresult status;
|
||||
nsresult rv = aRequest->GetStatus(&status);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (NS_FAILED(status)) {
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
// Test that things worked on a HTTP level
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
|
||||
NS_ENSURE_STATE(httpChannel);
|
||||
nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
|
||||
NS_ENSURE_STATE(internal);
|
||||
|
||||
bool succeedded;
|
||||
rv = httpChannel->GetRequestSucceeded(&succeedded);
|
||||
if (NS_FAILED(rv) || !succeedded) {
|
||||
// If the request did not return a 2XX response, don't process it
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
bool synthesized = false;
|
||||
nsHttpChannel* rawHttpChannel = static_cast<nsHttpChannel*>(httpChannel.get());
|
||||
rv = rawHttpChannel->GetResponseSynthesized(&synthesized);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (synthesized) {
|
||||
// Don't consider synthesized responses
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
// check to see if the HSTS cache was updated
|
||||
nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = httpChannel->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_CONTENT_BLOCKED);
|
||||
|
||||
bool hsts;
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0, nullptr, &hsts);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (hsts) {
|
||||
// An HSTS upgrade was found
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// There is no HSTS upgrade available
|
||||
return NS_ERROR_CONTENT_BLOCKED;
|
||||
}
|
||||
|
||||
/** nsIStreamListener methods **/
|
||||
|
||||
NS_IMETHODIMP
|
||||
HSTSPrimingListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
nsISupports *ctxt,
|
||||
nsIInputStream *inStr,
|
||||
uint64_t sourceOffset,
|
||||
uint32_t count)
|
||||
{
|
||||
uint32_t totalRead;
|
||||
return inStr->ReadSegments(NS_DiscardSegment, nullptr, count, &totalRead);
|
||||
}
|
||||
|
||||
// static
|
||||
nsresult
|
||||
HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
|
||||
nsIHstsPrimingCallback* aCallback)
|
||||
{
|
||||
|
||||
nsCOMPtr<nsIURI> finalChannelURI;
|
||||
nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(finalChannelURI));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = finalChannelURI->Clone(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = uri->SetScheme(NS_LITERAL_CSTRING("https"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// check the HSTS cache
|
||||
bool hsts;
|
||||
bool cached;
|
||||
nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0, &cached, &hsts);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (hsts) {
|
||||
// already saw this host and will upgrade if allowed by preferences
|
||||
return aCallback->OnHSTSPrimingSucceeded(true);
|
||||
}
|
||||
|
||||
if (cached) {
|
||||
// there is a non-expired entry in the cache that doesn't allow us to
|
||||
// upgrade, so go ahead and fail early.
|
||||
return aCallback->OnHSTSPrimingFailed(NS_ERROR_CONTENT_BLOCKED, true);
|
||||
}
|
||||
|
||||
// Either it wasn't cached or the cached result has expired. Build a
|
||||
// channel for the HEAD request.
|
||||
|
||||
nsCOMPtr<nsILoadInfo> originalLoadInfo = aRequestChannel->GetLoadInfo();
|
||||
MOZ_ASSERT(originalLoadInfo, "can not perform HSTS priming without a loadInfo");
|
||||
if (!originalLoadInfo) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = static_cast<mozilla::LoadInfo*>
|
||||
(originalLoadInfo.get())->CloneForNewRequest();
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsLoadFlags loadFlags;
|
||||
rv = aRequestChannel->GetLoadFlags(&loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
loadFlags &= HttpBaseChannel::INHIBIT_CACHING |
|
||||
HttpBaseChannel::INHIBIT_PERSISTENT_CACHING |
|
||||
HttpBaseChannel::LOAD_BYPASS_CACHE |
|
||||
HttpBaseChannel::LOAD_FROM_CACHE |
|
||||
HttpBaseChannel::VALIDATE_ALWAYS;
|
||||
// Priming requests should never be intercepted by service workers and
|
||||
// are always anonymous.
|
||||
loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
|
||||
nsIRequest::LOAD_ANONYMOUS;
|
||||
|
||||
// Create a new channel to send the priming request
|
||||
nsCOMPtr<nsIChannel> primingChannel;
|
||||
rv = NS_NewChannelInternal(getter_AddRefs(primingChannel),
|
||||
uri,
|
||||
loadInfo,
|
||||
loadGroup,
|
||||
nullptr, // aCallbacks are set later
|
||||
loadFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set method and headers
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(primingChannel);
|
||||
if (!httpChannel) {
|
||||
NS_ERROR("HSTSPrimingListener: Failed to QI to nsIHttpChannel!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Currently using HEAD per the draft, but under discussion to change to GET
|
||||
// with credentials so if the upgrade is approved the result is already cached.
|
||||
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = httpChannel->
|
||||
SetRequestHeader(NS_LITERAL_CSTRING("Upgrade-Insecure-Requests"),
|
||||
NS_LITERAL_CSTRING("1"), false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// attempt to set the class of service flags on the new channel
|
||||
nsCOMPtr<nsIClassOfService> requestClass = do_QueryInterface(aRequestChannel);
|
||||
if (!requestClass) {
|
||||
NS_ERROR("HSTSPrimingListener: aRequestChannel is not an nsIClassOfService");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIClassOfService> primingClass = do_QueryInterface(httpChannel);
|
||||
if (!primingClass) {
|
||||
NS_ERROR("HSTSPrimingListener: aRequestChannel is not an nsIClassOfService");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
uint32_t classFlags = 0;
|
||||
rv = requestClass ->GetClassFlags(&classFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = primingClass->SetClassFlags(classFlags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Set up listener which will start the original channel
|
||||
RefPtr<HSTSPrimingListener> primingListener =
|
||||
new HSTSPrimingListener(aCallback);
|
||||
|
||||
// Start priming
|
||||
rv = primingChannel->AsyncOpen2(primingListener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef HSTSPrimingListener_h__
|
||||
#define HSTSPrimingListener_h__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIThreadRetargetableStreamListener.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
class nsINetworkInterceptController;
|
||||
class nsIHstsPrimingCallback;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class HttpChannelParent;
|
||||
class nsHttpChannel;
|
||||
|
||||
/*
|
||||
* How often do we get back an HSTS priming result which upgrades the connection to HTTPS?
|
||||
*/
|
||||
enum HSTSPrimingResult {
|
||||
// This site has been seen before and won't be upgraded
|
||||
eHSTS_PRIMING_CACHED_NO_UPGRADE = 0,
|
||||
// This site has been seen before and will be upgraded
|
||||
eHSTS_PRIMING_CACHED_DO_UPGRADE = 1,
|
||||
// This site has been seen before and will be blocked
|
||||
eHSTS_PRIMING_CACHED_BLOCK = 2,
|
||||
// The request was already upgraded, probably through
|
||||
// upgrade-insecure-requests
|
||||
eHSTS_PRIMING_ALREADY_UPGRADED = 3,
|
||||
// HSTS priming is successful and the connection will be upgraded to HTTPS
|
||||
eHSTS_PRIMING_SUCCEEDED = 4,
|
||||
// When priming succeeds, but preferences require preservation of the order
|
||||
// of mixed-content and hsts, and mixed-content blocks the load
|
||||
eHSTS_PRIMING_SUCCEEDED_BLOCK = 5,
|
||||
// When priming succeeds, but preferences require preservation of the order
|
||||
// of mixed-content and hsts, and mixed-content allows the load over http
|
||||
eHSTS_PRIMING_SUCCEEDED_HTTP = 6,
|
||||
// HSTS priming failed, and the load is blocked by mixed-content
|
||||
eHSTS_PRIMING_FAILED_BLOCK = 7,
|
||||
// HSTS priming failed, and the load is allowed by mixed-content
|
||||
eHSTS_PRIMING_FAILED_ACCEPT = 8
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Class used as streamlistener and notification callback when
|
||||
// doing the HEAD request for an HSTS Priming check. Needs to be an
|
||||
// nsIStreamListener in order to receive events from AsyncOpen2
|
||||
class HSTSPrimingListener final : public nsIStreamListener,
|
||||
public nsIInterfaceRequestor
|
||||
{
|
||||
public:
|
||||
explicit HSTSPrimingListener(nsIHstsPrimingCallback* aCallback)
|
||||
: mCallback(aCallback)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
private:
|
||||
~HSTSPrimingListener() {}
|
||||
|
||||
// Only nsHttpChannel can invoke HSTS priming
|
||||
friend class mozilla::net::nsHttpChannel;
|
||||
|
||||
/**
|
||||
* Start the HSTS priming request. This will send an anonymous HEAD request to
|
||||
* the URI aRequestChannel is attempting to load. On success, the new HSTS
|
||||
* priming channel is allocated in aHSTSPrimingChannel.
|
||||
*
|
||||
* @param aRequestChannel the reference channel used to initialze the HSTS
|
||||
* priming channel
|
||||
* @param aCallback the callback stored to handle the results of HSTS priming.
|
||||
* @param aHSTSPrimingChannel if the new HSTS priming channel is allocated
|
||||
* successfully, it will be placed here.
|
||||
*/
|
||||
static nsresult StartHSTSPriming(nsIChannel* aRequestChannel,
|
||||
nsIHstsPrimingCallback* aCallback);
|
||||
|
||||
/**
|
||||
* Given a request, return NS_OK if it has resulted in a cached HSTS update.
|
||||
* We don't need to check for the header as that has already been done for us.
|
||||
*/
|
||||
nsresult CheckHSTSPrimingRequestStatus(nsIRequest* aRequest);
|
||||
|
||||
/**
|
||||
* the nsIHttpChannel to notify with the result of HSTS priming.
|
||||
*/
|
||||
nsCOMPtr<nsIHstsPrimingCallback> mCallback;
|
||||
};
|
||||
|
||||
|
||||
}} // mozilla::net
|
||||
|
||||
#endif // HSTSPrimingListener_h__
|
|
@ -5,6 +5,7 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIHstsPrimingCallback.idl',
|
||||
'nsIHttpActivityObserver.idl',
|
||||
'nsIHttpAuthenticableChannel.idl',
|
||||
'nsIHttpAuthenticator.idl',
|
||||
|
@ -54,6 +55,7 @@ SOURCES += [
|
|||
UNIFIED_SOURCES += [
|
||||
'CacheControlParser.cpp',
|
||||
'ConnectionDiagnostics.cpp',
|
||||
'HSTSPrimerListener.cpp',
|
||||
'Http2Compression.cpp',
|
||||
'Http2Push.cpp',
|
||||
'Http2Session.cpp',
|
||||
|
|
|
@ -98,6 +98,8 @@
|
|||
#include "nsISocketProvider.h"
|
||||
#include "mozilla/net/Predictor.h"
|
||||
#include "CacheControlParser.h"
|
||||
#include "nsMixedContentBlocker.h"
|
||||
#include "HSTSPrimerListener.h"
|
||||
|
||||
namespace mozilla { namespace net {
|
||||
|
||||
|
@ -405,12 +407,50 @@ nsHttpChannel::Connect()
|
|||
// otherwise, let's just proceed without using the cache.
|
||||
}
|
||||
|
||||
return TryHSTSPriming();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpChannel::TryHSTSPriming()
|
||||
{
|
||||
if (mLoadInfo) {
|
||||
// HSTS priming requires the LoadInfo provided with AsyncOpen2
|
||||
bool requireHSTSPriming =
|
||||
mLoadInfo->GetForceHSTSPriming();
|
||||
|
||||
if (requireHSTSPriming &&
|
||||
nsMixedContentBlocker::sSendHSTSPriming &&
|
||||
mInterceptCache == DO_NOT_INTERCEPT) {
|
||||
bool isHttpsScheme;
|
||||
nsresult rv = mURI->SchemeIs("https", &isHttpsScheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (!isHttpsScheme) {
|
||||
rv = HSTSPrimingListener::StartHSTSPriming(this, this);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
CloseCacheEntry(false);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The request was already upgraded, for example by
|
||||
// upgrade-insecure-requests or a prior successful priming request
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
|
||||
HSTSPrimingResult::eHSTS_PRIMING_ALREADY_UPGRADED);
|
||||
mLoadInfo->ClearHSTSPriming();
|
||||
}
|
||||
}
|
||||
|
||||
return ContinueConnect();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpChannel::ContinueConnect()
|
||||
{
|
||||
// If we have had HSTS priming, we need to reevaluate whether we need
|
||||
// a CORS preflight. Bug: 1272440
|
||||
// If we need to start a CORS preflight, do it now!
|
||||
// Note that it is important to do this before the early returns below.
|
||||
if (!mIsCorsPreflightDone && mRequireCORSPreflight &&
|
||||
|
@ -4018,7 +4058,7 @@ nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntry *entry,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
return ContinueConnect();
|
||||
return TryHSTSPriming();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -5358,6 +5398,7 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIHstsPrimingCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener)
|
||||
// we have no macro that covers this case.
|
||||
if (aIID.Equals(NS_GET_IID(nsHttpChannel)) ) {
|
||||
|
@ -7678,6 +7719,103 @@ nsHttpChannel::OnPreflightFailed(nsresult aError)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsIHstsPrimingCallback functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* May be invoked synchronously if HSTS priming has already been performed
|
||||
* for the host.
|
||||
*/
|
||||
nsresult
|
||||
nsHttpChannel::OnHSTSPrimingSucceeded(bool aCached)
|
||||
{
|
||||
if (nsMixedContentBlocker::sUseHSTS) {
|
||||
// redirect the channel to HTTPS if the pref
|
||||
// "security.mixed_content.use_hsts" is true
|
||||
LOG(("HSTS Priming succeeded, redirecting to HTTPS [this=%p]", this));
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
|
||||
(aCached) ? HSTSPrimingResult::eHSTS_PRIMING_CACHED_DO_UPGRADE :
|
||||
HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED);
|
||||
return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
|
||||
}
|
||||
|
||||
// If "security.mixed_content.use_hsts" is false, record the result of
|
||||
// HSTS priming and block or proceed with the load as required by
|
||||
// mixed-content blocking
|
||||
bool wouldBlock = mLoadInfo->GetMixedContentWouldBlock();
|
||||
|
||||
// preserve the mixed-content-before-hsts order and block if required
|
||||
if (wouldBlock) {
|
||||
LOG(("HSTS Priming succeeded, blocking for mixed-content [this=%p]",
|
||||
this));
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
|
||||
HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_BLOCK);
|
||||
CloseCacheEntry(false);
|
||||
return AsyncAbort(NS_ERROR_CONTENT_BLOCKED);
|
||||
}
|
||||
|
||||
LOG(("HSTS Priming succeeded, loading insecure: [this=%p]", this));
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
|
||||
HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_HTTP);
|
||||
|
||||
nsresult rv = ContinueConnect();
|
||||
if (NS_FAILED(rv)) {
|
||||
CloseCacheEntry(false);
|
||||
return AsyncAbort(rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* May be invoked synchronously if HSTS priming has already been performed
|
||||
* for the host.
|
||||
*/
|
||||
nsresult
|
||||
nsHttpChannel::OnHSTSPrimingFailed(nsresult aError, bool aCached)
|
||||
{
|
||||
bool wouldBlock = mLoadInfo->GetMixedContentWouldBlock();
|
||||
|
||||
LOG(("HSTS Priming Failed [this=%p], %s the load", this,
|
||||
(wouldBlock) ? "blocking" : "allowing"));
|
||||
if (aCached) {
|
||||
// Between the time we marked for priming and started the priming request,
|
||||
// the host was found to not allow the upgrade, probably from another
|
||||
// priming request.
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
|
||||
(wouldBlock) ? HSTSPrimingResult::eHSTS_PRIMING_CACHED_BLOCK :
|
||||
HSTSPrimingResult::eHSTS_PRIMING_CACHED_NO_UPGRADE);
|
||||
} else {
|
||||
// A priming request was sent, and no HSTS header was found that allows
|
||||
// the upgrade.
|
||||
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
|
||||
(wouldBlock) ? HSTSPrimingResult::eHSTS_PRIMING_FAILED_BLOCK :
|
||||
HSTSPrimingResult::eHSTS_PRIMING_FAILED_ACCEPT);
|
||||
}
|
||||
|
||||
// Don't visit again for at least one day
|
||||
nsISiteSecurityService* sss = gHttpHandler->GetSSService();
|
||||
NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsresult rv = sss->CacheNegativeHSTSResult(mURI, 24 * 60 * 60);
|
||||
NS_WARN_IF(NS_FAILED(rv));
|
||||
|
||||
// If we would block, go ahead and abort with the error provided
|
||||
if (wouldBlock) {
|
||||
CloseCacheEntry(false);
|
||||
return AsyncAbort(aError);
|
||||
}
|
||||
|
||||
// we can continue the load and the UI has been updated as mixed content
|
||||
rv = ContinueConnect();
|
||||
if (NS_FAILED(rv)) {
|
||||
CloseCacheEntry(false);
|
||||
return AsyncAbort(rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// AChannelHasDivertableParentChannelAsListener internal functions
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "nsIStreamListener.h"
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsICorsPreflightCallback.h"
|
||||
#include "nsIHstsPrimingCallback.h"
|
||||
|
||||
class nsDNSPrefetch;
|
||||
class nsICancelable;
|
||||
|
@ -74,6 +75,7 @@ class nsHttpChannel final : public HttpBaseChannel
|
|||
, public nsSupportsWeakReference
|
||||
, public nsICorsPreflightCallback
|
||||
, public nsIChannelWithDivertableParentListener
|
||||
, public nsIHstsPrimingCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
@ -89,6 +91,7 @@ public:
|
|||
NS_DECL_NSIAPPLICATIONCACHECONTAINER
|
||||
NS_DECL_NSIAPPLICATIONCACHECHANNEL
|
||||
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
|
||||
NS_DECL_NSIHSTSPRIMINGCALLBACK
|
||||
NS_DECL_NSITHREADRETARGETABLEREQUEST
|
||||
NS_DECL_NSIDNSLISTENER
|
||||
NS_DECL_NSICHANNELWITHDIVERTABLEPARENTLISTENER
|
||||
|
@ -203,6 +206,9 @@ public: /* internal necko use only */
|
|||
nsresult OpenCacheEntry(bool usingSSL);
|
||||
nsresult ContinueConnect();
|
||||
|
||||
// If the load is mixed-content, build and send an HSTS priming request.
|
||||
nsresult TryHSTSPriming();
|
||||
|
||||
nsresult StartRedirectChannelToURI(nsIURI *, uint32_t);
|
||||
|
||||
// This allows cache entry to be marked as foreign even after channel itself
|
||||
|
|
|
@ -2244,7 +2244,8 @@ nsHttpHandler::SpeculativeConnectInternal(nsIURI *aURI,
|
|||
flags |= nsISocketProvider::NO_PERMANENT_STORAGE;
|
||||
nsCOMPtr<nsIURI> clone;
|
||||
if (NS_SUCCEEDED(sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS,
|
||||
aURI, flags, &isStsHost)) && isStsHost) {
|
||||
aURI, flags, nullptr, &isStsHost)) &&
|
||||
isStsHost) {
|
||||
if (NS_SUCCEEDED(NS_GetSecureUpgradedURI(aURI,
|
||||
getter_AddRefs(clone)))) {
|
||||
aURI = clone.get();
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
/**
|
||||
* HSTS priming attempts to prevent mixed-content by looking for the
|
||||
* Strict-Transport-Security header as a signal from the server that it is
|
||||
* safe to upgrade HTTP to HTTPS.
|
||||
*
|
||||
* Since mixed-content blocking happens very early in the process in AsyncOpen2,
|
||||
* the status of mixed-content blocking is stored in the LoadInfo and then used
|
||||
* to determine whether to send a priming request or not.
|
||||
*
|
||||
* This interface is implemented by nsHttpChannel so that it can receive the
|
||||
* result of HSTS priming.
|
||||
*/
|
||||
[builtinclass, uuid(eca6daca-3f2a-4a2a-b3bf-9f24f79bc999)]
|
||||
interface nsIHstsPrimingCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* HSTS priming has succeeded with an STS header, and the site asserts it is
|
||||
* safe to upgrade the request from HTTP to HTTPS. The request may still be
|
||||
* blocked based on the user's preferences.
|
||||
*
|
||||
* May be invoked synchronously if HSTS priming has already been performed
|
||||
* for the host.
|
||||
*
|
||||
* @param aCached whether the result was already in the HSTS cache
|
||||
*/
|
||||
[noscript, nostdcall]
|
||||
void onHSTSPrimingSucceeded(in bool aCached);
|
||||
/**
|
||||
* HSTS priming has seen no STS header, the request itself has failed,
|
||||
* or some other failure which does not constitute a positive signal that the
|
||||
* site can be upgraded safely to HTTPS. The request may still be allowed
|
||||
* based on the user's preferences.
|
||||
*
|
||||
* May be invoked synchronously if HSTS priming has already been performed
|
||||
* for the host.
|
||||
*
|
||||
* param aError The error which caused this failure, or NS_ERROR_CONTENT_BLOCKED
|
||||
* @param aCached whether the result was already in the HSTS cache
|
||||
*/
|
||||
[noscript, nostdcall]
|
||||
void onHSTSPrimingFailed(in nsresult aError, in bool aCached);
|
||||
};
|
|
@ -506,6 +506,7 @@ CertErrorRunnable::CheckCertOverrides()
|
|||
nsresult nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HSTS,
|
||||
mInfoObject->GetHostNameRaw(),
|
||||
mProviderFlags,
|
||||
nullptr,
|
||||
&strictTransportSecurityEnabled);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
|
@ -516,6 +517,7 @@ CertErrorRunnable::CheckCertOverrides()
|
|||
nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HPKP,
|
||||
mInfoObject->GetHostNameRaw(),
|
||||
mProviderFlags,
|
||||
nullptr,
|
||||
&hasPinningInformation);
|
||||
if (NS_FAILED(nsrv)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
|
|
|
@ -115,10 +115,13 @@ interface nsISiteSecurityService : nsISupports
|
|||
* @param aHost the hostname (punycode) to query for state.
|
||||
* @param aFlags options for this request as defined in nsISocketProvider:
|
||||
* NO_PERMANENT_STORAGE
|
||||
* @param aCached true if we have cached information regarding whether or not
|
||||
* the host is HSTS, false otherwise.
|
||||
*/
|
||||
boolean isSecureHost(in uint32_t aType,
|
||||
in string aHost,
|
||||
in uint32_t aFlags);
|
||||
in uint32_t aFlags,
|
||||
[optional] out boolean aCached);
|
||||
|
||||
/**
|
||||
* Checks whether or not the URI's hostname has a given security state set.
|
||||
|
@ -133,8 +136,11 @@ interface nsISiteSecurityService : nsISupports
|
|||
* @param aURI the URI to query for STS state.
|
||||
* @param aFlags options for this request as defined in nsISocketProvider:
|
||||
* NO_PERMANENT_STORAGE
|
||||
* @param aCached true if we have cached information regarding whether or not
|
||||
* the host is HSTS, false otherwise.
|
||||
*/
|
||||
boolean isSecureURI(in uint32_t aType, in nsIURI aURI, in uint32_t aFlags);
|
||||
boolean isSecureURI(in uint32_t aType, in nsIURI aURI, in uint32_t aFlags,
|
||||
[optional] out boolean aCached);
|
||||
|
||||
/**
|
||||
* Removes all security state by resetting to factory-original settings.
|
||||
|
@ -174,6 +180,14 @@ interface nsISiteSecurityService : nsISupports
|
|||
in unsigned long aMaxAge, in unsigned long aPinCount,
|
||||
[array, size_is(aPinCount)] in string aSha256Pins);
|
||||
|
||||
/**
|
||||
* Mark a host as declining to provide a given security state so that features
|
||||
* such as HSTS priming will not flood a server with requests.
|
||||
*
|
||||
* @param aHost the hostname (punycode) that this applies to
|
||||
* @param aMaxAge lifetime (in seconds) of this negative cache
|
||||
*/
|
||||
[noscript] void cacheNegativeHSTSResult(in nsIURI aURI, in unsigned long long aMaxAge);
|
||||
};
|
||||
|
||||
%{C++
|
||||
|
|
|
@ -314,7 +314,8 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType,
|
|||
nsIURI* aSourceURI,
|
||||
int64_t maxage,
|
||||
bool includeSubdomains,
|
||||
uint32_t flags)
|
||||
uint32_t flags,
|
||||
SecurityPropertyState aHSTSState)
|
||||
{
|
||||
// If max-age is zero, that's an indication to immediately remove the
|
||||
// security state, so here's a shortcut.
|
||||
|
@ -322,8 +323,12 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType,
|
|||
return RemoveState(aType, aSourceURI, flags);
|
||||
}
|
||||
|
||||
MOZ_ASSERT((aHSTSState == SecurityPropertySet
|
||||
|| aHSTSState == SecurityPropertyNegative),
|
||||
"HSTS State must be SecurityPropertySet or SecurityPropertyNegative");
|
||||
|
||||
int64_t expiretime = ExpireTimeFromMaxAge(maxage);
|
||||
SiteHSTSState siteState(expiretime, SecurityPropertySet, includeSubdomains);
|
||||
SiteHSTSState siteState(expiretime, aHSTSState, includeSubdomains);
|
||||
nsAutoCString stateString;
|
||||
siteState.ToString(stateString);
|
||||
nsAutoCString hostname;
|
||||
|
@ -342,6 +347,14 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSiteSecurityService::CacheNegativeHSTSResult(nsIURI* aSourceURI,
|
||||
uint64_t aMaxAge)
|
||||
{
|
||||
return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, aSourceURI,
|
||||
aMaxAge, false, 0, SecurityPropertyNegative);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI,
|
||||
uint32_t aFlags)
|
||||
|
@ -864,7 +877,7 @@ nsSiteSecurityService::ProcessSTSHeader(nsIURI* aSourceURI,
|
|||
|
||||
// record the successfully parsed header data.
|
||||
nsresult rv = SetHSTSState(aType, aSourceURI, maxAge, foundIncludeSubdomains,
|
||||
aFlags);
|
||||
aFlags, SecurityPropertySet);
|
||||
if (NS_FAILED(rv)) {
|
||||
SSSLOG(("SSS: failed to set STS state"));
|
||||
if (aFailureResult) {
|
||||
|
@ -888,7 +901,8 @@ nsSiteSecurityService::ProcessSTSHeader(nsIURI* aSourceURI,
|
|||
|
||||
NS_IMETHODIMP
|
||||
nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
|
||||
uint32_t aFlags, bool* aResult)
|
||||
uint32_t aFlags, bool* aCached,
|
||||
bool* aResult)
|
||||
{
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
|
||||
|
@ -912,7 +926,7 @@ nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
return IsSecureHost(aType, hostname.get(), aFlags, aResult);
|
||||
return IsSecureHost(aType, hostname.get(), aFlags, aCached, aResult);
|
||||
}
|
||||
|
||||
int STSPreloadCompare(const void *key, const void *entry)
|
||||
|
@ -942,7 +956,8 @@ nsSiteSecurityService::GetPreloadListEntry(const char *aHost)
|
|||
|
||||
NS_IMETHODIMP
|
||||
nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
||||
uint32_t aFlags, bool* aResult)
|
||||
uint32_t aFlags, bool* aCached,
|
||||
bool* aResult)
|
||||
{
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
|
||||
|
@ -959,6 +974,9 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
|
||||
// set default in case if we can't find any STS information
|
||||
*aResult = false;
|
||||
if (aCached) {
|
||||
*aCached = false;
|
||||
}
|
||||
|
||||
/* An IP address never qualifies as a secure URI. */
|
||||
if (HostIsIPAddress(aHost)) {
|
||||
|
@ -984,6 +1002,9 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
nsAutoCString host(PublicKeyPinningService::CanonicalizeHostname(aHost));
|
||||
if (host.EqualsLiteral("chart.apis.google.com") ||
|
||||
StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
|
||||
if (aCached) {
|
||||
*aCached = true;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1007,9 +1028,17 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
if (siteState.mHSTSState != SecurityPropertyUnset) {
|
||||
SSSLOG(("Found entry for %s", host.get()));
|
||||
bool expired = siteState.IsExpired(aType);
|
||||
if (!expired && siteState.mHSTSState == SecurityPropertySet) {
|
||||
*aResult = true;
|
||||
return NS_OK;
|
||||
if (!expired) {
|
||||
if (aCached) {
|
||||
*aCached = true;
|
||||
}
|
||||
if (siteState.mHSTSState == SecurityPropertySet) {
|
||||
*aResult = true;
|
||||
return NS_OK;
|
||||
} else if (siteState.mHSTSState == SecurityPropertyNegative) {
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// If the entry is expired and not in the preload list, we can remove it.
|
||||
|
@ -1022,6 +1051,9 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
else if (GetPreloadListEntry(host.get())) {
|
||||
SSSLOG(("%s is a preloaded STS host", host.get()));
|
||||
*aResult = true;
|
||||
if (aCached) {
|
||||
*aCached = true;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1054,9 +1086,17 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
if (siteState.mHSTSState != SecurityPropertyUnset) {
|
||||
SSSLOG(("Found entry for %s", subdomain));
|
||||
bool expired = siteState.IsExpired(aType);
|
||||
if (!expired && siteState.mHSTSState == SecurityPropertySet) {
|
||||
*aResult = siteState.mHSTSIncludeSubdomains;
|
||||
break;
|
||||
if (!expired) {
|
||||
if (aCached) {
|
||||
*aCached = true;
|
||||
}
|
||||
if (siteState.mHSTSState == SecurityPropertySet) {
|
||||
*aResult = siteState.mHSTSIncludeSubdomains;
|
||||
break;
|
||||
} else if (siteState.mHSTSState == SecurityPropertyNegative) {
|
||||
*aResult = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the entry is expired and not in the preload list, we can remove it.
|
||||
|
@ -1070,6 +1110,9 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
if (preload->mIncludeSubdomains) {
|
||||
SSSLOG(("%s is a preloaded STS host", subdomain));
|
||||
*aResult = true;
|
||||
if (aCached) {
|
||||
*aCached = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ class nsISSLStatus;
|
|||
enum SecurityPropertyState {
|
||||
SecurityPropertyUnset = 0,
|
||||
SecurityPropertySet = 1,
|
||||
SecurityPropertyKnockout = 2
|
||||
SecurityPropertyKnockout = 2,
|
||||
SecurityPropertyNegative = 3
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -128,7 +129,8 @@ protected:
|
|||
private:
|
||||
nsresult GetHost(nsIURI *aURI, nsACString &aResult);
|
||||
nsresult SetHSTSState(uint32_t aType, nsIURI* aSourceURI, int64_t maxage,
|
||||
bool includeSubdomains, uint32_t flags);
|
||||
bool includeSubdomains, uint32_t flags,
|
||||
SecurityPropertyState aHSTSState);
|
||||
nsresult ProcessHeaderInternal(uint32_t aType, nsIURI* aSourceURI,
|
||||
const char* aHeader, nsISSLStatus* aSSLStatus,
|
||||
uint32_t aFlags, uint64_t* aMaxAge,
|
||||
|
|
|
@ -324,10 +324,10 @@ SpecialPowersObserverAPI.prototype = {
|
|||
case "BOOL":
|
||||
if (aMessage.json.op == "get")
|
||||
return(prefs.getBoolPref(prefName));
|
||||
else
|
||||
else
|
||||
return(prefs.setBoolPref(prefName, prefValue));
|
||||
case "INT":
|
||||
if (aMessage.json.op == "get")
|
||||
if (aMessage.json.op == "get")
|
||||
return(prefs.getIntPref(prefName));
|
||||
else
|
||||
return(prefs.setIntPref(prefName, prefValue));
|
||||
|
@ -563,6 +563,7 @@ SpecialPowersObserverAPI.prototype = {
|
|||
let sss = Cc["@mozilla.org/ssservice;1"].
|
||||
getService(Ci.nsISiteSecurityService);
|
||||
sss.removeState(Ci.nsISiteSecurityService.HEADER_HSTS, uri, flags);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
case "SPLoadExtension": {
|
||||
|
|
|
@ -7316,6 +7316,22 @@
|
|||
"n_values": 10,
|
||||
"description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS"
|
||||
},
|
||||
"MIXED_CONTENT_HSTS_PRIMING": {
|
||||
"alert_emails": ["seceng@mozilla.org"],
|
||||
"bug_numbers": [1246540],
|
||||
"expires_in_version": "60",
|
||||
"kind": "enumerated",
|
||||
"n_values": 16,
|
||||
"description": "How often would blocked mixed content be allowed if HSTS upgrades were allowed, including how often would we send an HSTS priming request? 0=display/no-HSTS, 1=display/HSTS, 2=active/no-HSTS, 3=active/HSTS, 4=display/no-HSTS-priming, 5=display/do-HSTS-priming, 6=active/no-HSTS-priming, 7=active/do-HSTS-priming"
|
||||
},
|
||||
"MIXED_CONTENT_HSTS_PRIMING_RESULT": {
|
||||
"alert_emails": ["seceng@mozilla.org"],
|
||||
"bug_numbers": [1246540],
|
||||
"expires_in_version": "60",
|
||||
"kind": "enumerated",
|
||||
"n_values": 16,
|
||||
"description": "How often do we get back an HSTS priming result which upgrades the connection to HTTPS? 0=cached (no upgrade), 1=cached (do upgrade), 2=cached (blocked), 3=already upgraded, 4=priming succeeded, 5=priming succeeded (block due to pref), 6=priming succeeded (no upgrade due to pref), 7=priming failed (block), 8=priming failed (accept)"
|
||||
},
|
||||
"MIXED_CONTENT_OBJECT_SUBREQUEST": {
|
||||
"alert_emails": ["seceng@mozilla.org"],
|
||||
"bug_numbers": [1244116],
|
||||
|
|
Загрузка…
Ссылка в новой задаче