Bug 1363546 - Store and report HSTS upgrade source r=francois,keeler,mayhemer p=francois

Add a field to the HSTS cache which indicates the source of the HSTS
entry if known, from the preload list, organically seen header, or HSTS
priming, or unknown otherwise. Also adds telemetry to collect the source
when upgrading in NS_ShouldSecureUpgrade.

MozReview-Commit-ID: 3IwyYe3Cn73

--HG--
extra : rebase_source : 9b8daac3aa02bd7a1b4285fb1e5731a817a76b7f
This commit is contained in:
Kate McKinley 2017-05-23 15:31:37 -07:00
Родитель d8f53f83da
Коммит 396962011a
39 изменённых файлов: 325 добавлений и 93 удалений

Просмотреть файл

@ -5031,10 +5031,11 @@ 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, mOriginAttributes, nullptr, &isStsHost);
flags, mOriginAttributes, nullptr, nullptr,
&isStsHost);
NS_ENSURE_SUCCESS(rv, rv);
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
flags, mOriginAttributes, nullptr,
flags, mOriginAttributes, nullptr, nullptr,
&isPinnedHost);
NS_ENSURE_SUCCESS(rv, rv);
} else {

Просмотреть файл

@ -3560,7 +3560,7 @@ ContentParent::RecvIsSecureURI(const uint32_t& aType,
return IPC_FAIL_NO_REASON(this);
}
nsresult rv = sss->IsSecureURI(aType, ourURI, aFlags, aOriginAttributes, nullptr,
aIsSecureURI);
nullptr, aIsSecureURI);
if (NS_FAILED(rv)) {
return IPC_FAIL_NO_REASON(this);
}

Просмотреть файл

@ -924,7 +924,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aContentLocation,
0, originAttributes, &cached, &hsts);
0, originAttributes, &cached, nullptr, &hsts);
NS_ENSURE_SUCCESS(rv, rv);
if (hsts && sUseHSTS) {
@ -1150,7 +1150,7 @@ nsMixedContentBlocker::AccumulateMixedContentHSTS(
return;
}
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, 0,
aOriginAttributes, nullptr, &hsts);
aOriginAttributes, nullptr, nullptr, &hsts);
if (NS_FAILED(rv)) {
return;
}

Просмотреть файл

@ -9,6 +9,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
"HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -9,6 +9,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
"HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -9,6 +9,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
"HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -9,6 +9,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
"HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -9,6 +9,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
"HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -9,6 +9,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
"HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -32,9 +32,9 @@ add_task(function*() {
let pre_promise = performance.now();
while ((performance.now() - pre_promise) < 2000) {
while ((performance.now() - pre_promise) < 1000) {
yield new Promise(function (resolve) {
setTimeout(resolve, 2000);
setTimeout(resolve, 1000);
});
}

Просмотреть файл

@ -9,6 +9,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
"HSTS_UPGRADE_SOURCE": [ 0,0,0,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -14,6 +14,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 2,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 4,
"HSTS_UPGRADE_SOURCE": [ 0,0,2,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -9,6 +9,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 8,
"HSTS_UPGRADE_SOURCE": [ 0,0,2,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -10,6 +10,7 @@ var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 1,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 3,
"HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {

Просмотреть файл

@ -447,6 +447,7 @@ async function execute_test(test, mimetype) {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 6,
"HSTS_PRIMING_REQUESTS": 10,
"HSTS_UPGRADE_SOURCE": [ 0,0,2,0,0,0,0,0,0 ]
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
@ -473,8 +474,24 @@ function test_telemetry(expected) {
continue;
}
// there may have been other background requests processed
ok(hs.counts.reduce(sum) >= expected['histograms'][key], "Histogram counts match expected, got " + hs.counts.reduce(sum) + ", expected at least " + expected['histograms'][key]);
if (Array.isArray(expected['histograms'][key])) {
var is_ok = true;
if (expected['histograms'][key].length != hs.counts.length) {
ok(false, "Histogram lengths match");
continue;
}
for (let idx in expected['histograms'][key]) {
is_ok = (hs.counts[idx] >= expected['histograms'][key][idx]);
if (!is_ok) {
break;
}
}
ok(is_ok, "Histogram counts match for " + key + " - Got " + hs.counts + ", expected " + expected['histograms'][key]);
} else {
// there may have been other background requests processed
ok(hs.counts.reduce(sum) >= expected['histograms'][key], "Histogram counts match expected, got " + hs.counts.reduce(sum) + ", expected at least " + expected['histograms'][key]);
}
}
for (let key in expected['keyed-histograms']) {

Просмотреть файл

@ -2584,9 +2584,10 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
bool isStsHost = false;
uint32_t hstsSource = 0;
uint32_t flags = aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags,
aOriginAttributes, nullptr, &isStsHost);
aOriginAttributes, nullptr, &hstsSource, &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
@ -2598,6 +2599,22 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
if (aAllowSTS) {
Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3);
aShouldUpgrade = true;
switch (hstsSource) {
case nsISiteSecurityService::SOURCE_PRELOAD_LIST:
Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 0);
break;
case nsISiteSecurityService::SOURCE_ORGANIC_REQUEST:
Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1);
break;
case nsISiteSecurityService::SOURCE_HSTS_PRIMING:
Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 2);
break;
case nsISiteSecurityService::SOURCE_UNKNOWN:
default:
// record this as an organic request
Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 1);
break;
}
return NS_OK;
} else {
Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 2);

Просмотреть файл

@ -158,7 +158,7 @@ HSTSPrimingListener::CheckHSTSPrimingRequestStatus(nsIRequest* aRequest)
bool hsts;
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0,
originAttributes, nullptr, &hsts);
originAttributes, nullptr, nullptr, &hsts);
NS_ENSURE_SUCCESS(rv, rv);
if (hsts) {
@ -227,7 +227,7 @@ HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
// check the HSTS cache
bool hsts;
bool cached;
bool hstsCached;
nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
@ -235,7 +235,7 @@ HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
NS_GetOriginAttributes(aRequestChannel, originAttributes);
rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, 0,
originAttributes, &cached, &hsts);
originAttributes, &hstsCached, nullptr, &hsts);
NS_ENSURE_SUCCESS(rv, rv);
if (hsts) {
@ -245,7 +245,7 @@ HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
return aCallback->OnHSTSPrimingSucceeded(true);
}
if (cached) {
if (hstsCached) {
// there is a non-expired entry in the cache that doesn't allow us to
// upgrade, so go ahead and fail early.
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,

Просмотреть файл

@ -1796,9 +1796,13 @@ nsHttpChannel::ProcessSingleSecurityHeader(uint32_t aType,
OriginAttributes originAttributes;
NS_GetOriginAttributes(this, originAttributes);
uint32_t failureResult;
uint32_t headerSource = nsISiteSecurityService::SOURCE_ORGANIC_REQUEST;
if (mLoadInfo && mLoadInfo->GetIsHSTSPriming()) {
headerSource = nsISiteSecurityService::SOURCE_HSTS_PRIMING;
}
rv = sss->ProcessHeader(aType, mURI, securityHeader, aSSLStatus,
aFlags, originAttributes, nullptr, nullptr,
&failureResult);
aFlags, headerSource, originAttributes,
nullptr, nullptr, &failureResult);
if (NS_FAILED(rv)) {
nsAutoString consoleErrorCategory;
nsAutoString consoleErrorTag;
@ -8660,8 +8664,10 @@ nsHttpChannel::OnHSTSPrimingSucceeded(bool aCached)
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
(aCached) ? HSTSPrimingResult::eHSTS_PRIMING_CACHED_DO_UPGRADE :
HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED);
// we have to record this upgrade here
// we have to record this upgrade here since we have already
// been through NS_ShouldSecureUpgrade
Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3);
Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 2);
mLoadInfo->SetIsHSTSPrimingUpgrade(true);
return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
}

Просмотреть файл

@ -2412,7 +2412,7 @@ nsHttpHandler::SpeculativeConnectInternal(nsIURI *aURI,
nsCOMPtr<nsIURI> clone;
if (NS_SUCCEEDED(sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS,
aURI, flags, originAttributes,
nullptr, &isStsHost)) &&
nullptr, nullptr, &isStsHost)) &&
isStsHost) {
if (NS_SUCCEEDED(NS_GetSecureUpgradedURI(aURI,
getter_AddRefs(clone)))) {

Просмотреть файл

@ -500,6 +500,7 @@ CertErrorRunnable::OverrideAllowedForHost(/*out*/ bool& overrideAllowed)
mProviderFlags,
mInfoObject->GetOriginAttributes(),
nullptr,
nullptr,
&strictTransportSecurityEnabled);
if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
@ -511,6 +512,7 @@ CertErrorRunnable::OverrideAllowedForHost(/*out*/ bool& overrideAllowed)
mProviderFlags,
mInfoObject->GetOriginAttributes(),
nullptr,
nullptr,
&hasPinningInformation);
if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,

Просмотреть файл

@ -91,6 +91,16 @@ interface nsISiteSecurityService : nsISupports
const uint32_t ERROR_COULD_NOT_SAVE_STATE = 13;
const uint32_t ERROR_ROOT_NOT_BUILT_IN = 14;
/**
* nsISiteSecurityService::IsSecureURI can optionally return a flag
* indicating the source of the HSTS cache entry, if it comes from the
* preload list, was seen naturally, or is a result of HSTS priming.
*/
const uint32_t SOURCE_UNKNOWN = 0;
const uint32_t SOURCE_PRELOAD_LIST = 1;
const uint32_t SOURCE_ORGANIC_REQUEST = 2;
const uint32_t SOURCE_HSTS_PRIMING = 3;
/**
* Parses a given HTTP header and records the results internally.
* Currently two header types are supported: HSTS (aka STS) and HPKP
@ -114,6 +124,8 @@ interface nsISiteSecurityService : nsISupports
* by userContextId because of the risk of man-in-
* the-middle attacks before trust-on-second-use
* happens).
* @param aSource the source of the header, whether it was from the preload
* list, an organic header, or HSTS priming, or unknown.
* @param aMaxAge the parsed max-age directive of the header.
* @param aIncludeSubdomains the parsed includeSubdomains directive.
* @param aFailureResult a more specific failure result if NS_ERROR_FAILURE
@ -129,6 +141,7 @@ interface nsISiteSecurityService : nsISupports
in ACString aHeader,
in nsISSLStatus aSSLStatus,
in uint32_t aFlags,
in uint32_t aSource,
in const_OriginAttributesRef aOriginAttributes,
[optional] out unsigned long long aMaxAge,
[optional] out boolean aIncludeSubdomains,
@ -141,6 +154,7 @@ interface nsISiteSecurityService : nsISupports
in ACString aHeader,
in nsISSLStatus aSSLStatus,
in uint32_t aFlags,
in uint32_t aSource,
[optional] in jsval aOriginAttributes,
[optional] out unsigned long long aMaxAge,
[optional] out boolean aIncludeSubdomains,
@ -185,20 +199,25 @@ interface nsISiteSecurityService : nsISupports
* by userContextId because of the risk of man-in-
* the-middle attacks before trust-on-second-use
* happens).
* @param aCached true if we have cached information regarding whether or not
* the host is HSTS, false otherwise.
* @param aCached true if we have cached information about this host, even
* if the security state is false.
* @param aSource the source of the HSTS entry. One of SOURCE_PRELOAD_LIST,
* SOURCE_ORGANIC_REQUEST, SOURCE_HSTS_PRIMING, or
* SOURCE_UNKNOWN. Not implemented for HPKP.
*/
[binaryname(IsSecureURI), noscript, must_use]
boolean isSecureURINative(in uint32_t aType, in nsIURI aURI,
in uint32_t aFlags,
in const_OriginAttributesRef aOriginAttributes,
[optional] out boolean aCached);
[optional] out boolean aCached,
[optional] out uint32_t aSource);
[binaryname(IsSecureURIScriptable), implicit_jscontext, optional_argc,
must_use]
boolean isSecureURI(in uint32_t aType, in nsIURI aURI, in uint32_t aFlags,
[optional] in jsval aOriginAttributes,
[optional] out boolean aCached);
[optional] out boolean aCached,
[optional] out uint32_t aSource);
/**
* Removes all non-preloaded security state by resetting to factory-original

Просмотреть файл

@ -116,6 +116,28 @@ public:
return true;
}
MOZ_MUST_USE bool
ReadSource(/*out*/ SecurityPropertySource& source)
{
uint32_t rawValue;
if (!ReadInteger(&rawValue)) {
return false;
}
source = static_cast<SecurityPropertySource>(rawValue);
switch (source) {
case SourceUnknown:
case SourcePreload:
case SourceOrganic:
case SourceHSTSPriming:
break;
default:
return false;
}
return true;
}
// Note: Ideally, this method would be able to read SHA256 strings without
// reading all the way to EOF. Unfortunately, if a token starts with digits
// mozilla::Tokenizer will by default not consider the digits part of the
@ -153,9 +175,11 @@ bool
ParseHSTSState(const nsCString& stateString,
/*out*/ PRTime& expireTime,
/*out*/ SecurityPropertyState& state,
/*out*/ bool& includeSubdomains)
/*out*/ bool& includeSubdomains,
/*out*/ SecurityPropertySource& source)
{
SSSTokenizer tokenizer(stateString);
SSSLOG(("Parsing state from %s", stateString.get()));
if (!tokenizer.ReadInteger(&expireTime)) {
return false;
@ -177,6 +201,13 @@ ParseHSTSState(const nsCString& stateString,
return false;
}
source = SourceUnknown;
if (tokenizer.CheckChar(',')) {
if (!tokenizer.ReadSource(source)) {
return false;
}
}
return tokenizer.CheckEOF();
}
@ -190,14 +221,16 @@ SiteHSTSState::SiteHSTSState(const nsCString& aHost,
, mHSTSExpireTime(0)
, mHSTSState(SecurityPropertyUnset)
, mHSTSIncludeSubdomains(false)
, mHSTSSource(SourceUnknown)
{
bool valid = ParseHSTSState(aStateString, mHSTSExpireTime, mHSTSState,
mHSTSIncludeSubdomains);
mHSTSIncludeSubdomains, mHSTSSource);
if (!valid) {
SSSLOG(("%s is not a valid SiteHSTSState", aStateString.get()));
mHSTSExpireTime = 0;
mHSTSState = SecurityPropertyUnset;
mHSTSIncludeSubdomains = false;
mHSTSSource = SourceUnknown;
}
}
@ -205,13 +238,15 @@ SiteHSTSState::SiteHSTSState(const nsCString& aHost,
const OriginAttributes& aOriginAttributes,
PRTime aHSTSExpireTime,
SecurityPropertyState aHSTSState,
bool aHSTSIncludeSubdomains)
bool aHSTSIncludeSubdomains,
SecurityPropertySource aSource)
: mHostname(aHost)
, mOriginAttributes(aOriginAttributes)
, mHSTSExpireTime(aHSTSExpireTime)
, mHSTSState(aHSTSState)
, mHSTSIncludeSubdomains(aHSTSIncludeSubdomains)
, mHSTSSource(aSource)
{
}
@ -224,6 +259,8 @@ SiteHSTSState::ToString(nsCString& aString)
aString.AppendInt(mHSTSState);
aString.Append(',');
aString.AppendInt(static_cast<uint32_t>(mHSTSIncludeSubdomains));
aString.Append(',');
aString.AppendInt(mHSTSSource);
}
NS_IMETHODIMP
@ -581,27 +618,29 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType,
bool includeSubdomains,
uint32_t flags,
SecurityPropertyState aHSTSState,
bool aIsPreload,
SecurityPropertySource aSource,
const OriginAttributes& aOriginAttributes)
{
nsAutoCString hostname(aHost);
bool isPreload = (aSource == SourcePreload);
// If max-age is zero, that's an indication to immediately remove the
// security state, so here's a shortcut.
if (!maxage) {
return RemoveStateInternal(aType, hostname, flags, aIsPreload,
return RemoveStateInternal(aType, hostname, flags, isPreload,
aOriginAttributes);
}
MOZ_ASSERT((aHSTSState == SecurityPropertySet ||
aHSTSState == SecurityPropertyNegative),
"HSTS State must be SecurityPropertySet or SecurityPropertyNegative");
if (aIsPreload && aOriginAttributes != OriginAttributes()) {
if (isPreload && aOriginAttributes != OriginAttributes()) {
return NS_ERROR_INVALID_ARG;
}
int64_t expiretime = ExpireTimeFromMaxAge(maxage);
RefPtr<SiteHSTSState> siteState = new SiteHSTSState(
hostname, aOriginAttributes, expiretime, aHSTSState, includeSubdomains);
hostname, aOriginAttributes, expiretime, aHSTSState, includeSubdomains,
aSource);
nsAutoCString stateString;
siteState->ToString(stateString);
SSSLOG(("SSS: setting state for %s", hostname.get()));
@ -612,12 +651,21 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType,
nsAutoCString storageKey;
SetStorageKey(hostname, aType, aOriginAttributes, storageKey);
nsresult rv;
if (aIsPreload) {
if (isPreload) {
SSSLOG(("SSS: storing entry for %s in dynamic preloads", hostname.get()));
rv = mPreloadStateStorage->Put(storageKey, stateString,
mozilla::DataStorage_Persistent);
} else {
SSSLOG(("SSS: storing HSTS site entry for %s", hostname.get()));
nsCString value = mSiteStateStorage->Get(storageKey, storageType);
RefPtr<SiteHSTSState> curSiteState =
new SiteHSTSState(hostname, aOriginAttributes, value);
if (curSiteState->mHSTSState != SecurityPropertyUnset &&
curSiteState->mHSTSSource != SourceUnknown) {
// don't override the source
siteState->mHSTSSource = curSiteState->mHSTSSource;
siteState->ToString(stateString);
}
rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
}
NS_ENSURE_SUCCESS(rv, rv);
@ -634,9 +682,10 @@ nsSiteSecurityService::CacheNegativeHSTSResult(
nsAutoCString hostname;
nsresult rv = GetHost(aSourceURI, hostname);
NS_ENSURE_SUCCESS(rv, rv);
// SecurityPropertyNegative results only come from HSTS priming
return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, hostname.get(),
aMaxAge, false, 0, SecurityPropertyNegative, false,
aOriginAttributes);
aMaxAge, false, 0, SecurityPropertyNegative,
SourceHSTSPriming, aOriginAttributes);
}
nsresult
@ -685,7 +734,8 @@ nsSiteSecurityService::RemoveStateInternal(
dynamicState->mHSTSState != SecurityPropertyUnset) {
SSSLOG(("SSS: storing knockout entry for %s", aHost.get()));
RefPtr<SiteHSTSState> siteState = new SiteHSTSState(
aHost, aOriginAttributes, 0, SecurityPropertyKnockout, false);
aHost, aOriginAttributes, 0, SecurityPropertyKnockout, false,
SourceUnknown);
nsAutoCString stateString;
siteState->ToString(stateString);
nsresult rv;
@ -740,6 +790,7 @@ nsSiteSecurityService::ProcessHeaderScriptable(
const nsACString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
uint32_t aSource,
JS::HandleValue aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
@ -755,7 +806,7 @@ nsSiteSecurityService::ProcessHeaderScriptable(
}
}
return ProcessHeader(aType, aSourceURI, aHeader, aSSLStatus, aFlags,
originAttributes, aMaxAge, aIncludeSubdomains,
aSource, originAttributes, aMaxAge, aIncludeSubdomains,
aFailureResult);
}
@ -765,6 +816,7 @@ nsSiteSecurityService::ProcessHeader(uint32_t aType,
const nsACString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
uint32_t aHeaderSource,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
@ -782,11 +834,21 @@ nsSiteSecurityService::ProcessHeader(uint32_t aType,
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
aType == nsISiteSecurityService::HEADER_HPKP,
NS_ERROR_NOT_IMPLEMENTED);
SecurityPropertySource source = static_cast<SecurityPropertySource>(aHeaderSource);
switch (source) {
case SourceUnknown:
case SourcePreload:
case SourceOrganic:
case SourceHSTSPriming:
break;
default:
return NS_ERROR_INVALID_ARG;
}
NS_ENSURE_ARG(aSSLStatus);
return ProcessHeaderInternal(aType, aSourceURI, PromiseFlatCString(aHeader),
aSSLStatus, aFlags, aOriginAttributes, aMaxAge,
aIncludeSubdomains, aFailureResult);
aSSLStatus, aFlags, source, aOriginAttributes,
aMaxAge, aIncludeSubdomains, aFailureResult);
}
nsresult
@ -796,6 +858,7 @@ nsSiteSecurityService::ProcessHeaderInternal(
const nsCString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
SecurityPropertySource aSource,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
@ -851,8 +914,9 @@ nsSiteSecurityService::ProcessHeaderInternal(
switch (aType) {
case nsISiteSecurityService::HEADER_HSTS:
rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aOriginAttributes, aMaxAge,
aIncludeSubdomains, aFailureResult);
rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aSource,
aOriginAttributes, aMaxAge, aIncludeSubdomains,
aFailureResult);
break;
case nsISiteSecurityService::HEADER_HPKP:
rv = ProcessPKPHeader(aSourceURI, aHeader, aSSLStatus, aFlags,
@ -1187,6 +1251,7 @@ nsSiteSecurityService::ProcessSTSHeader(
nsIURI* aSourceURI,
const nsCString& aHeader,
uint32_t aFlags,
SecurityPropertySource aSource,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
@ -1230,7 +1295,7 @@ nsSiteSecurityService::ProcessSTSHeader(
// record the successfully parsed header data.
rv = SetHSTSState(aType, hostname.get(), maxAge, foundIncludeSubdomains,
aFlags, SecurityPropertySet, false, aOriginAttributes);
aFlags, SecurityPropertySet, aSource, aOriginAttributes);
if (NS_FAILED(rv)) {
SSSLOG(("SSS: failed to set STS state"));
if (aFailureResult) {
@ -1256,7 +1321,8 @@ NS_IMETHODIMP
nsSiteSecurityService::IsSecureURIScriptable(uint32_t aType, nsIURI* aURI,
uint32_t aFlags,
JS::HandleValue aOriginAttributes,
bool* aCached, JSContext* aCx,
bool* aCached,
uint32_t* aSource, JSContext* aCx,
uint8_t aArgc, bool* aResult)
{
OriginAttributes originAttributes;
@ -1266,14 +1332,15 @@ nsSiteSecurityService::IsSecureURIScriptable(uint32_t aType, nsIURI* aURI,
return NS_ERROR_INVALID_ARG;
}
}
return IsSecureURI(aType, aURI, aFlags, originAttributes, aCached, aResult);
return IsSecureURI(aType, aURI, aFlags, originAttributes, aCached, aSource, aResult);
}
NS_IMETHODIMP
nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
bool* aCached, bool* aResult)
bool* aCached,
uint32_t* aSource, bool* aResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
@ -1297,8 +1364,10 @@ nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
return NS_OK;
}
SecurityPropertySource* source = BitwiseCast<SecurityPropertySource*>(aSource);
return IsSecureHost(aType, hostname, aFlags, aOriginAttributes, aCached,
aResult);
source, aResult);
}
int STSPreloadCompare(const void *key, const void *entry)
@ -1335,8 +1404,15 @@ nsSiteSecurityService::GetPreloadListEntry(const char *aHost)
bool
nsSiteSecurityService::HostHasHSTSEntry(
const nsAutoCString& aHost, bool aRequireIncludeSubdomains, uint32_t aFlags,
const OriginAttributes& aOriginAttributes, bool* aResult, bool* aCached)
const OriginAttributes& aOriginAttributes, bool* aResult, bool* aCached,
SecurityPropertySource* aSource)
{
if (aSource) {
*aSource = SourceUnknown;
}
if (aCached) {
*aCached = false;
}
// First we check for an entry in site security storage. If that entry exists,
// we don't want to check in the preload lists. We only want to use the
// stored value if it is not a knockout entry, however.
@ -1370,13 +1446,20 @@ nsSiteSecurityService::HostHasHSTSEntry(
*aCached = aRequireIncludeSubdomains ? siteState->mHSTSIncludeSubdomains
: true;
}
if (aSource) {
*aSource = siteState->mHSTSSource;
}
return true;
} else if (siteState->mHSTSState == SecurityPropertyNegative) {
*aResult = false;
if (aCached) {
// if it's negative, it is always cached
SSSLOG(("Marking HSTS as as cached (SecurityPropertyNegative)"));
*aCached = true;
}
if (aSource) {
*aSource = siteState->mHSTSSource;
}
return true;
}
}
@ -1419,6 +1502,9 @@ nsSiteSecurityService::HostHasHSTSEntry(
*aCached = aRequireIncludeSubdomains ? dynamicState->mHSTSIncludeSubdomains
: true;
}
if (aSource) {
*aSource = dynamicState->mHSTSSource;
}
return true;
} else if (dynamicState->mHSTSState == SecurityPropertyNegative) {
*aResult = false;
@ -1426,6 +1512,9 @@ nsSiteSecurityService::HostHasHSTSEntry(
// if it's negative, it is always cached
*aCached = true;
}
if (aSource) {
*aSource = dynamicState->mHSTSSource;
}
return true;
}
} else {
@ -1450,9 +1539,12 @@ nsSiteSecurityService::HostHasHSTSEntry(
: true;
if (aCached) {
// Only set cached if this includes subdomains
*aCached = aRequireIncludeSubdomains ? siteState->mHSTSIncludeSubdomains
*aCached = aRequireIncludeSubdomains ? preload->mIncludeSubdomains
: true;
}
if (aSource) {
*aSource = SourcePreload;
}
return true;
}
@ -1463,7 +1555,9 @@ nsresult
nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
bool* aCached, bool* aResult)
bool* aCached,
SecurityPropertySource* aSource,
bool* aResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
@ -1480,9 +1574,6 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& 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. */
const nsCString& flatHost = PromiseFlatCString(aHost);
@ -1515,11 +1606,15 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
if (aCached) {
*aCached = true;
}
if (aSource) {
*aSource = SourcePreload;
}
return NS_OK;
}
// First check the exact host.
if (HostHasHSTSEntry(host, false, aFlags, aOriginAttributes, aResult, aCached)) {
if (HostHasHSTSEntry(host, false, aFlags, aOriginAttributes, aResult,
aCached, aSource)) {
return NS_OK;
}
@ -1545,7 +1640,7 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
nsAutoCString subdomainString(subdomain);
if (HostHasHSTSEntry(subdomainString, true, aFlags, aOriginAttributes, aResult,
aCached)) {
aCached, aSource)) {
break;
}
@ -1718,8 +1813,8 @@ nsSiteSecurityService::SetHSTSPreload(const nsACString& aHost,
nsAutoCString host(
PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, host.get(), aExpires,
aIncludeSubdomains, 0, SecurityPropertySet, true,
OriginAttributes());
aIncludeSubdomains, 0, SecurityPropertySet,
SourcePreload, OriginAttributes());
}
nsresult

Просмотреть файл

@ -42,6 +42,13 @@ enum SecurityPropertyState {
SecurityPropertyNegative = nsISiteSecurityState::SECURITY_PROPERTY_NEGATIVE,
};
enum SecurityPropertySource {
SourceUnknown = nsISiteSecurityService::SOURCE_UNKNOWN,
SourcePreload = nsISiteSecurityService::SOURCE_PRELOAD_LIST,
SourceOrganic = nsISiteSecurityService::SOURCE_ORGANIC_REQUEST,
SourceHSTSPriming = nsISiteSecurityService::SOURCE_HSTS_PRIMING,
};
/**
* SiteHPKPState: A utility class that encodes/decodes a string describing
* the public key pins of a site.
@ -114,13 +121,15 @@ public:
SiteHSTSState(const nsCString& aHost,
const OriginAttributes& aOriginAttributes,
PRTime aHSTSExpireTime, SecurityPropertyState aHSTSState,
bool aHSTSIncludeSubdomains);
bool aHSTSIncludeSubdomains,
SecurityPropertySource aSource);
nsCString mHostname;
OriginAttributes mOriginAttributes;
PRTime mHSTSExpireTime;
SecurityPropertyState mHSTSState;
bool mHSTSIncludeSubdomains;
SecurityPropertySource mHSTSSource;
bool IsExpired(uint32_t aType)
{
@ -164,17 +173,20 @@ private:
nsresult GetHost(nsIURI *aURI, nsACString &aResult);
nsresult SetHSTSState(uint32_t aType, const char* aHost, int64_t maxage,
bool includeSubdomains, uint32_t flags,
SecurityPropertyState aHSTSState, bool aIsPreload,
SecurityPropertyState aHSTSState,
SecurityPropertySource aSource,
const OriginAttributes& aOriginAttributes);
nsresult ProcessHeaderInternal(uint32_t aType, nsIURI* aSourceURI,
const nsCString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
SecurityPropertySource aSource,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge, bool* aIncludeSubdomains,
uint32_t* aFailureResult);
nsresult ProcessSTSHeader(nsIURI* aSourceURI, const nsCString& aHeader,
uint32_t flags,
SecurityPropertySource aSource,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge, bool* aIncludeSubdomains,
uint32_t* aFailureResult);
@ -194,12 +206,14 @@ private:
bool HostHasHSTSEntry(const nsAutoCString& aHost,
bool aRequireIncludeSubdomains, uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
bool* aResult, bool* aCached);
bool* aResult, bool* aCached,
SecurityPropertySource* aSource);
const nsSTSPreload *GetPreloadListEntry(const char *aHost);
nsresult IsSecureHost(uint32_t aType, const nsACString& aHost,
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
bool* aCached, bool* aResult);
bool* aCached, SecurityPropertySource* aSource,
bool* aResult);
uint64_t mMaxMaxAge;
bool mUsePreloadList;

Просмотреть файл

@ -53,7 +53,8 @@ function test() {
let sslStatus = new FakeSSLStatus();
uri = aWindow.Services.io.newURI("https://localhost/img.png");
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1000", sslStatus, privacyFlags(aIsPrivateMode));
"max-age=1000", sslStatus, privacyFlags(aIsPrivateMode),
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
privacyFlags(aIsPrivateMode)),
"checking sts host");

Просмотреть файл

@ -50,9 +50,11 @@ var sslStatus = new FakeSSLStatus(constructCertFromFile(
// to be HSTS or HPKP any longer.
add_task(function* () {
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, GOOD_MAX_AGE,
sslStatus, 0);
sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri,
GOOD_MAX_AGE + VALID_PIN + BACKUP_PIN, sslStatus, 0);
GOOD_MAX_AGE + VALID_PIN + BACKUP_PIN, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
Assert.ok(sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
"a.pinning2.example.com should be HSTS");
@ -73,9 +75,11 @@ add_task(function* () {
// unrelated sites don't also get removed.
add_task(function* () {
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, GOOD_MAX_AGE,
sslStatus, 0);
sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri,
GOOD_MAX_AGE + VALID_PIN + BACKUP_PIN, sslStatus, 0);
GOOD_MAX_AGE + VALID_PIN + BACKUP_PIN, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
Assert.ok(sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
"a.pinning2.example.com should be HSTS (subdomain case)");
@ -86,7 +90,8 @@ add_task(function* () {
// example.org.
let unrelatedURI = Services.io.newURI("https://example.org");
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, unrelatedURI,
GOOD_MAX_AGE, sslStatus, 0);
GOOD_MAX_AGE, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
Assert.ok(sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
unrelatedURI, 0), "example.org should be HSTS");
@ -119,9 +124,12 @@ add_task(function* () {
for (let originAttributes of originAttributesList) {
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, GOOD_MAX_AGE,
sslStatus, 0, originAttributes);
sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
originAttributes);
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri,
GOOD_MAX_AGE + VALID_PIN + BACKUP_PIN, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
originAttributes);
Assert.ok(sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
@ -133,7 +141,9 @@ add_task(function* () {
// Add an unrelated site to HSTS. Not HPKP because we have no valid keys.
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, unrelatedURI,
GOOD_MAX_AGE, sslStatus, 0, originAttributes);
GOOD_MAX_AGE, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
originAttributes);
Assert.ok(sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
unrelatedURI, 0, originAttributes),
"example.org should be HSTS (originAttributes case)");

Просмотреть файл

@ -38,7 +38,8 @@ function add_tests() {
.getService(Ci.nsISiteSecurityService);
let sslStatus = new FakeSSLStatus();
sslStatus.serverCert = constructCertFromFile("ocsp_certs/must-staple-ee-with-must-staple-int.pem");
ssservice.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri, header, sslStatus, 0);
ssservice.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri, header, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(ssservice.isSecureURI(Ci.nsISiteSecurityService.HEADER_HPKP, uri, 0),
"ocsp-stapling-must-staple-ee-with-must-staple-int.example.com should have HPKP set");

Просмотреть файл

@ -44,7 +44,8 @@ function run_test() {
let uri = Services.io.newURI("http://localhost");
let sslStatus = new FakeSSLStatus();
SSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=10000", sslStatus, 0);
"max-age=10000", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
"Domain for the OCSP AIA URI should be considered a HSTS host, otherwise" +
" we wouldn't be testing what we think we're testing");

Просмотреть файл

@ -28,7 +28,8 @@ function checkFailParseInvalidPin(pinValue) {
let uri = Services.io.newURI("https://a.pinning2.example.com");
throws(() => {
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri,
pinValue, sslStatus, 0);
pinValue, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
}, /NS_ERROR_FAILURE/, `Invalid pin "${pinValue}" should be rejected`);
}
@ -46,11 +47,14 @@ function checkPassValidPin(pinValue, settingPin, expectedMaxAge) {
// add a known valid pin!
let validPinValue = "max-age=5000;" + VALID_PIN1 + BACKUP_PIN1;
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri,
validPinValue, sslStatus, 0);
validPinValue, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
}
try {
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri,
pinValue, sslStatus, 0, {}, maxAge);
pinValue, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
{}, maxAge);
ok(true, "Valid pin should be accepted");
} catch (e) {
ok(false, "Valid pin should have been accepted");

Просмотреть файл

@ -52,12 +52,14 @@ function insertEntries() {
header += "; includeSubdomains";
}
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri, header,
sslStatus, 0);
sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
for (let key of KEY_HASHES) {
header += `; pin-sha256="${key}"`;
}
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HPKP, uri, header,
sslStatus, 0);
sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
}
}

Просмотреть файл

@ -58,7 +58,8 @@ function do_state_read(aSubject, aTopic, aData) {
for (let i = 0; i < 2000; i++) {
let uri = Services.io.newURI("http://bad" + i + ".example.com");
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1000", sslStatus, 0);
"max-age=1000", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
}
do_test_pending();
Services.obs.addObserver(do_state_written, "data-storage-written");

Просмотреть файл

@ -53,7 +53,9 @@ function doTest(originAttributes1, originAttributes2, shouldShare) {
header += VALID_PIN + BACKUP_PIN;
}
// Set HSTS or HPKP for originAttributes1.
sss.processHeader(type, uri, header, sslStatus, 0, originAttributes1);
sss.processHeader(type, uri, header, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
originAttributes1);
ok(sss.isSecureURI(type, uri, 0, originAttributes1),
"URI should be secure given original origin attributes");
equal(sss.isSecureURI(type, uri, 0, originAttributes2), shouldShare,
@ -100,6 +102,7 @@ function testInvalidOriginAttributes(originAttributes) {
let callbacks = [
() => sss.processHeader(type, uri, header, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST,
originAttributes),
() => sss.isSecureURI(type, uri, 0, originAttributes),
() => sss.removeState(type, uri, 0, originAttributes),

Просмотреть файл

@ -7,7 +7,7 @@
// writes its state file.
const EXPECTED_ENTRIES = 6;
const EXPECTED_HSTS_COLUMNS = 3;
const EXPECTED_HSTS_COLUMNS = 4;
const EXPECTED_HPKP_COLUMNS = 4;
var gProfileDir = null;
@ -118,7 +118,8 @@ function run_test() {
let sslStatus = new FakeSSLStatus();
SSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS,
uris[uriIndex], maxAge + includeSubdomains,
sslStatus, 0);
sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
}
do_test_pending();

Просмотреть файл

@ -17,7 +17,8 @@ function run_test() {
let sslStatus = new FakeSSLStatus();
SSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1000;includeSubdomains", sslStatus, 0);
"max-age=1000;includeSubdomains", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri1, 0));
ok(SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri2, 0));

Просмотреть файл

@ -19,7 +19,8 @@ function check_ip(s, v, ip) {
let parsedMaxAge = {};
let parsedIncludeSubdomains = {};
s.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1000;includeSubdomains", sslStatus, 0, {},
"max-age=1000;includeSubdomains", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST, {},
parsedMaxAge, parsedIncludeSubdomains);
ok(!s.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0),
"URI should not be secure if it contains an IP address");
@ -31,6 +32,8 @@ function check_ip(s, v, ip) {
*/
notEqual(parsedMaxAge.value, 1000);
notEqual(parsedIncludeSubdomains.value, true);
notEqual(parsedMaxAge.value, undefined);
notEqual(parsedIncludeSubdomains.value, undefined);
}
function run_test() {

Просмотреть файл

@ -17,7 +17,8 @@ function testSuccess(header, expectedMaxAge, expectedIncludeSubdomains) {
let includeSubdomains = {};
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, dummyUri, header,
sslStatus, 0, {}, maxAge, includeSubdomains);
sslStatus, 0, sss.SOURCE_ORGANIC_REQUEST, {}, maxAge,
includeSubdomains);
equal(maxAge.value, expectedMaxAge, "Did not correctly parse maxAge");
equal(includeSubdomains.value, expectedIncludeSubdomains,
@ -31,7 +32,8 @@ function testFailure(header) {
throws(() => {
sss.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, dummyUri, header,
sslStatus, 0, {}, maxAge, includeSubdomains);
sslStatus, 0, sss.SOURCE_ORGANIC_REQUEST, {}, maxAge,
includeSubdomains);
}, "Parsed invalid header: " + header);
}

Просмотреть файл

@ -60,7 +60,8 @@ function run_test() {
// Now let's simulate overriding the entry by setting an entry from a header
// with max-age set to 0
SSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=0", sslStatus, 0);
"max-age=0", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
// this should no longer be an HSTS host
ok(!SSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));

Просмотреть файл

@ -77,14 +77,16 @@ function test_part1() {
let subDomainUri =
Services.io.newURI("https://subdomain.includesubdomains.preloaded.test");
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=0", sslStatus, 0);
"max-age=0", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
subDomainUri, 0));
// check that processing another header (with max-age non-zero) will
// re-enable a site's sts status
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1000", sslStatus, 0);
"max-age=1000", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
// but this time include subdomains was not set, so test for that
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
@ -95,7 +97,8 @@ function test_part1() {
// will not remove that (ancestor) site from the list
uri = Services.io.newURI("https://subdomain.noincludesubdomains.preloaded.test");
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=0", sslStatus, 0);
"max-age=0", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
Services.io.newURI("https://noincludesubdomains.preloaded.test"),
0));
@ -103,7 +106,8 @@ function test_part1() {
uri = Services.io.newURI("https://subdomain.includesubdomains.preloaded.test");
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=0", sslStatus, 0);
"max-age=0", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
// we received a header with "max-age=0", so we have "no information"
// regarding the sts state of subdomain.includesubdomains.preloaded.test specifically,
// but it is actually still an STS host, because of the preloaded
@ -128,7 +132,8 @@ function test_part1() {
0));
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1000", sslStatus, 0);
"max-age=1000", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
// Here's what we have now:
// |-- includesubdomains.preloaded.test (in preload list, includes subdomains) IS sts host
// |-- subdomain.includesubdomains.preloaded.test (include subdomains is false) IS sts host
@ -154,7 +159,8 @@ function test_part1() {
uri = Services.io.newURI("https://includesubdomains2.preloaded.test");
ok(gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1", sslStatus, 0);
"max-age=1", sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
do_timeout(1250, function() {
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, 0));
run_next_test();
@ -175,7 +181,8 @@ function test_private_browsing1() {
IS_PRIVATE));
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=0", sslStatus, IS_PRIVATE);
"max-age=0", sslStatus, IS_PRIVATE,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
IS_PRIVATE));
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
@ -183,7 +190,8 @@ function test_private_browsing1() {
// check adding it back in
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1000", sslStatus, IS_PRIVATE);
"max-age=1000", sslStatus, IS_PRIVATE,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri, IS_PRIVATE));
// but no includeSubdomains this time
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
@ -191,7 +199,8 @@ function test_private_browsing1() {
// do the hokey-pokey...
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=0", sslStatus, IS_PRIVATE);
"max-age=0", sslStatus, IS_PRIVATE,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
IS_PRIVATE));
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS,
@ -207,7 +216,8 @@ function test_private_browsing1() {
ok(gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
IS_PRIVATE));
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
"max-age=1", sslStatus, IS_PRIVATE);
"max-age=1", sslStatus, IS_PRIVATE,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST);
do_timeout(1250, function() {
ok(!gSSService.isSecureURI(Ci.nsISiteSecurityService.HEADER_HSTS, uri,
IS_PRIVATE));

Просмотреть файл

@ -116,8 +116,9 @@ function processStsHeader(host, header, status, securityInfo) {
var sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
.SSLStatus;
gSSService.processHeader(Ci.nsISiteSecurityService.HEADER_HSTS,
uri, header, sslStatus, 0, {}, maxAge,
includeSubdomains);
uri, header, sslStatus, 0,
Ci.nsISiteSecurityService.SOURCE_PRELOAD_LIST,
{}, maxAge, includeSubdomains);
} catch (e) {
dump("ERROR: could not process header '" + header + "' from " +
host.name + ": " + e + "\n");

Просмотреть файл

@ -10230,6 +10230,15 @@
"keyed": true,
"description": "The amount of time required for HSTS priming requests (ms), keyed by success or failure of the priming request. (success, failure)"
},
"HSTS_UPGRADE_SOURCE": {
"record_in_processes": [ "main" ],
"alert_emails": ["seceng-telemetry@mozilla.com"],
"bug_numbers": [1363546],
"expires_in_version": "62",
"kind": "enumerated",
"n_values": 8,
"description": "When we record an upgrade due to HSTS, record the source of that HSTS entry. (0: HSTS preload list, 1: HSTS Header seen naturally, 2: HSTS priming)"
},
"MIXED_CONTENT_OBJECT_SUBREQUEST": {
"record_in_processes": ["main", "content"],
"alert_emails": ["seceng-telemetry@mozilla.com"],