Bug 1359987 - Update HSTS priming telemetry r=ckerschb,francois,mayhemer p=francois

Collect telemetry for all requests to get an exact percentage of
requests that are subject to HSTS priming, and how many result in an
HSTS Priming request being sent. Clean up telemetry to remove instances
of double counting requests if a priming request was sent.

HSTSPrimingListener::ReportTiming was using mCallback to calculate
timing telemetry, but we were calling swap() on the nsCOMPtr. Give it an
explicit argument for the callback.

Add tests for telemetry values to all of the HSTS priming tests. This
tests for the minimum as telemetry may be gathered on background or
other requests.

MozReview-Commit-ID: 5V2Nf0Ugc3r

--HG--
extra : rebase_source : daa357219a77d912a78b95a703430f39d884c6ab
This commit is contained in:
Kate McKinley 2017-05-09 15:36:07 -07:00
parent 6d3eb2c851
commit 37a7ace256
25 changed files with 539 additions and 71 deletions

View File

@ -90,6 +90,29 @@ IsEligibleForHSTSPriming(nsIURI* aContentLocation) {
return (PR_StringToNetAddr(hostname.get(), &hostAddr) != PR_SUCCESS);
}
enum MixedContentHSTSState {
MCB_HSTS_PASSIVE_NO_HSTS = 0,
MCB_HSTS_PASSIVE_WITH_HSTS = 1,
MCB_HSTS_ACTIVE_NO_HSTS = 2,
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,
eMCB_HSTS_PASSIVE_UPGRADE = 6,
eMCB_HSTS_ACTIVE_UPGRADE = 7,
};
// 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.
@ -892,6 +915,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
originAttributes = aRequestPrincipal->OriginAttributesRef();
}
bool active = (classification == eMixedScript);
bool doHSTSPriming = false;
if (IsEligibleForHSTSPriming(aContentLocation)) {
bool hsts = false;
@ -905,6 +929,9 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
if (hsts && sUseHSTS) {
// assume we will be upgraded later
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
(active) ? MixedContentHSTSPrimingState::eMCB_HSTS_ACTIVE_UPGRADE
: MixedContentHSTSPrimingState::eMCB_HSTS_PASSIVE_UPGRADE);
*aDecision = ACCEPT;
return NS_OK;
}
@ -932,7 +959,6 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
//
// We do not count requests aHadInsecureImageRedirect=true, since these are
// just an artifact of the image caching system.
bool active = (classification == eMixedScript);
if (!aHadInsecureImageRedirect) {
if (XRE_IsParentProcess()) {
AccumulateMixedContentHSTS(innerContentLocation, active, doHSTSPriming,
@ -1103,27 +1129,6 @@ nsMixedContentBlocker::ShouldProcess(uint32_t aContentType,
aDecision);
}
enum MixedContentHSTSState {
MCB_HSTS_PASSIVE_NO_HSTS = 0,
MCB_HSTS_PASSIVE_WITH_HSTS = 1,
MCB_HSTS_ACTIVE_NO_HSTS = 2,
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
@ -1158,17 +1163,17 @@ nsMixedContentBlocker::AccumulateMixedContentHSTS(
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
MCB_HSTS_PASSIVE_NO_HSTS);
if (aHasHSTSPriming) {
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
eMCB_HSTS_PASSIVE_DO_PRIMING);
} else {
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
eMCB_HSTS_PASSIVE_NO_PRIMING);
}
}
else {
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
MCB_HSTS_PASSIVE_WITH_HSTS);
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
eMCB_HSTS_PASSIVE_WITH_HSTS);
}
} else {
@ -1176,17 +1181,17 @@ nsMixedContentBlocker::AccumulateMixedContentHSTS(
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
MCB_HSTS_ACTIVE_NO_HSTS);
if (aHasHSTSPriming) {
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
eMCB_HSTS_ACTIVE_DO_PRIMING);
} else {
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
eMCB_HSTS_ACTIVE_NO_PRIMING);
}
}
else {
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
MCB_HSTS_ACTIVE_WITH_HSTS);
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
eMCB_HSTS_ACTIVE_WITH_HSTS);
}
}

View File

@ -5,6 +5,19 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -15,10 +28,13 @@ add_task(function*() {
let which = "allow_active";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,19 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -15,10 +28,13 @@ add_task(function*() {
let which = "allow_display";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,19 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -15,10 +28,13 @@ add_task(function*() {
let which = "block_active";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,19 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -15,10 +28,13 @@ add_task(function*() {
let which = "block_active_css";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,19 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -15,10 +28,13 @@ add_task(function*() {
let which = "block_active_with_redir_same";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,19 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -15,10 +28,13 @@ add_task(function*() {
let which = "block_display";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,18 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 2,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 4,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -14,6 +26,7 @@ add_task(function*() {
let which = "block_display";
SetupPrefTestEnvironment(which, [["security.mixed_content.hsts_priming_cache_timeout", 1]]);
clear_hists(expected_telemetry);
yield execute_test("no-ssl", test_settings[which].mimetype);
@ -32,5 +45,7 @@ add_task(function*() {
is(test_settings[which].priming["no-ssl"], true,
"Correctly send a priming request after expiration.");
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,19 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -15,10 +28,13 @@ add_task(function*() {
let which = "hsts_after_mixed";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -10,6 +10,18 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 2,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 4,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -27,6 +39,7 @@ add_task(function*() {
let which = "block_active";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
yield execute_test("top-level", test_settings[which].mimetype);
@ -35,5 +48,7 @@ add_task(function*() {
ok("prime-hsts" in test_settings[which].priming,
"HSTS priming on a subdomain when top-level does not includeSubDomains");
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,19 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 8,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -14,6 +27,7 @@ add_task(function*() {
let which = "block_display";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
@ -24,5 +38,7 @@ add_task(function*() {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -6,6 +6,15 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 0,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 1,
},
"keyed-histograms": {
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -23,8 +32,11 @@ add_task(function*() {
let which = "block_active";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
yield execute_test("localhost-ip", test_settings[which].mimetype);
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -6,6 +6,18 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 1,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 3,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -14,7 +26,7 @@ add_task(function*() {
// add the top-level server
test_servers['non-standard-port'] = {
host: 'example.com:1234',
host: 'test1.example.com:1234',
response: true,
id: 'non-standard-port',
};
@ -23,6 +35,7 @@ add_task(function*() {
let which = "block_active";
SetupPrefTestEnvironment(which);
clear_hists(expected_telemetry);
yield execute_test("non-standard-port", test_settings[which].mimetype);
@ -30,5 +43,7 @@ add_task(function*() {
ok("prime-hsts" in test_settings[which_test].priming, "Sent priming request on standard port after non-standard was not primed");
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -5,6 +5,18 @@
*/
'use strict';
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 3,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"failure": 3,
},
}
};
//jscs:disable
add_task(function*() {
//jscs:enable
@ -15,10 +27,13 @@ add_task(function*() {
SetupPrefTestEnvironment(which, [["security.mixed_content.hsts_priming_request_timeout",
1000]]);
clear_hists(expected_telemetry);
for (let server of Object.keys(test_servers)) {
yield execute_test(server, test_settings[which].mimetype);
}
test_telemetry(expected_telemetry);
SpecialPowers.popPrefEnv();
});

View File

@ -377,12 +377,16 @@ function clear_sts_data() {
}
}
var oldCanRecord = Services.telemetry.canRecordExtended;
function do_cleanup() {
clear_sts_data();
Services.obs.removeObserver(Observer, "console-api-log-event");
Services.obs.removeObserver(Observer, "http-on-examine-response");
Services.telemetry.canRecordExtended = oldCanRecord;
Observer.cleanup();
}
@ -404,6 +408,7 @@ function SetupPrefTestEnvironment(which, additional_prefs) {
settings.use_hsts],
["security.mixed_content.send_hsts_priming",
settings.send_hsts_priming],
["toolkit.telemetry.enabled", true],
];
if (additional_prefs) {
@ -412,6 +417,8 @@ function SetupPrefTestEnvironment(which, additional_prefs) {
}
}
Services.telemetry.canRecordExtended = true;
SpecialPowers.pushPrefEnv({'set': prefs});
}
@ -433,3 +440,88 @@ async function execute_test(test, mimetype) {
await BrowserTestUtils.withNewTab(src, () => {});
}
/* Expected should look something like this:
* The numbers are the sum of all telemetry values.
var expected_telemetry = {
"histograms": {
"MIXED_CONTENT_HSTS_PRIMING_RESULT": 6,
"HSTS_PRIMING_REQUESTS": 10,
},
"keyed-histograms": {
"HSTS_PRIMING_REQUEST_DURATION": {
"success": 1,
"failure": 2,
},
}
};
*/
function test_telemetry(expected) {
for (let key in expected['histograms']) {
let hs = undefined;
try {
let hist = Services.telemetry.getHistogramById(key);
hs = hist.snapshot();
hist.clear();
} catch(e) {
ok(false, "Caught exception trying to get histogram for key " + key + ":" + e);
continue;
}
if (!hs) {
ok(false, "No histogram found for key " + key);
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]);
}
for (let key in expected['keyed-histograms']) {
let hs = undefined;
try {
let hist = Services.telemetry.getKeyedHistogramById(key);
hs = hist.snapshot();
hist.clear();
} catch(e) {
ok(false, "Caught exception trying to get histogram for key " + key + " :" + e);
continue;
}
if (!hs) {
ok(false, "No keyed histogram found for key " + key);
continue;
}
for (let hist_key in expected['keyed-histograms'][key]) {
ok(hist_key in hs, "Keyed histogram exists with key");
if (hist_key in hs) {
ok(hs[hist_key].counts.reduce(sum) >= expected['keyed-histograms'][key][hist_key], "Keyed histogram counts match expected got " + hs[hist_key].counts.reduce(sum) + ", expected at least " + expected['keyed-histograms'][key][hist_key])
}
}
}
}
function sum(a, b) {
return a+b;
}
function clear_hists(hists) {
for (let key in hists['histograms']) {
try {
let hist = Services.telemetry.getHistogramById(key);
hist.clear();
} catch(e) {
continue;
}
}
for (let key in hists['keyed-histograms']) {
try {
let hist = Services.telemetry.getKeyedHistogramById(key);
hist.clear();
} catch(e) {
continue;
}
}
}

View File

@ -389,7 +389,10 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoadInfo,
aLoadInfo->GetForcePreflight(),
aLoadInfo->GetIsPreflight(),
aLoadInfo->GetForceHSTSPriming(),
aLoadInfo->GetMixedContentWouldBlock());
aLoadInfo->GetMixedContentWouldBlock(),
aLoadInfo->GetIsHSTSPriming(),
aLoadInfo->GetIsHSTSPrimingUpgrade()
);
return NS_OK;
}
@ -474,7 +477,9 @@ LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
loadInfoArgs.forcePreflight(),
loadInfoArgs.isPreflight(),
loadInfoArgs.forceHSTSPriming(),
loadInfoArgs.mixedContentWouldBlock()
loadInfoArgs.mixedContentWouldBlock(),
loadInfoArgs.isHSTSPriming(),
loadInfoArgs.isHSTSPrimingUpgrade()
);
loadInfo.forget(outLoadInfo);

View File

@ -58,6 +58,8 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mIsPreflight(false)
, mForceHSTSPriming(false)
, mMixedContentWouldBlock(false)
, mIsHSTSPriming(false)
, mIsHSTSPrimingUpgrade(false)
{
MOZ_ASSERT(mLoadingPrincipal);
MOZ_ASSERT(mTriggeringPrincipal);
@ -231,6 +233,8 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
, mIsPreflight(false)
, mForceHSTSPriming(false)
, mMixedContentWouldBlock(false)
, mIsHSTSPriming(false)
, mIsHSTSPrimingUpgrade(false)
{
// Top-level loads are never third-party
// Grab the information we can out of the window.
@ -294,6 +298,8 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
, mIsPreflight(rhs.mIsPreflight)
, mForceHSTSPriming(rhs.mForceHSTSPriming)
, mMixedContentWouldBlock(rhs.mMixedContentWouldBlock)
, mIsHSTSPriming(rhs.mIsHSTSPriming)
, mIsHSTSPrimingUpgrade(rhs.mIsHSTSPrimingUpgrade)
{
}
@ -322,7 +328,9 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
bool aForcePreflight,
bool aIsPreflight,
bool aForceHSTSPriming,
bool aMixedContentWouldBlock)
bool aMixedContentWouldBlock,
bool aIsHSTSPriming,
bool aIsHSTSPrimingUpgrade)
: mLoadingPrincipal(aLoadingPrincipal)
, mTriggeringPrincipal(aTriggeringPrincipal)
, mPrincipalToInherit(aPrincipalToInherit)
@ -346,6 +354,8 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadingPrincipal,
, mIsPreflight(aIsPreflight)
, mForceHSTSPriming (aForceHSTSPriming)
, mMixedContentWouldBlock(aMixedContentWouldBlock)
, mIsHSTSPriming(aIsHSTSPriming)
, mIsHSTSPrimingUpgrade(aIsHSTSPrimingUpgrade)
{
// Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
MOZ_ASSERT(mLoadingPrincipal || aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
@ -933,6 +943,38 @@ LoadInfo::ClearHSTSPriming()
mMixedContentWouldBlock = false;
}
NS_IMETHODIMP
LoadInfo::SetIsHSTSPriming(bool aIsHSTSPriming)
{
MOZ_ASSERT(aIsHSTSPriming);
mIsHSTSPriming = aIsHSTSPriming;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetIsHSTSPriming(bool* aIsHSTSPriming)
{
MOZ_ASSERT(aIsHSTSPriming);
*aIsHSTSPriming = mIsHSTSPriming;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::SetIsHSTSPrimingUpgrade(bool aIsHSTSPrimingUpgrade)
{
MOZ_ASSERT(aIsHSTSPrimingUpgrade);
mIsHSTSPrimingUpgrade = aIsHSTSPrimingUpgrade;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetIsHSTSPrimingUpgrade(bool* aIsHSTSPrimingUpgrade)
{
MOZ_ASSERT(aIsHSTSPrimingUpgrade);
*aIsHSTSPrimingUpgrade = mIsHSTSPrimingUpgrade;
return NS_OK;
}
NS_IMETHODIMP
LoadInfo::GetTainting(uint32_t* aTaintingOut)
{

View File

@ -115,7 +115,9 @@ private:
bool aForcePreflight,
bool aIsPreflight,
bool aForceHSTSPriming,
bool aMixedContentWouldBlock);
bool aMixedContentWouldBlock,
bool aIsHSTSPriming,
bool aIsHSTSPrimingUpgrade);
LoadInfo(const LoadInfo& rhs);
NS_IMETHOD GetRedirects(JSContext* aCx, JS::MutableHandle<JS::Value> aRedirects,
@ -165,6 +167,8 @@ private:
bool mForceHSTSPriming : 1;
bool mMixedContentWouldBlock : 1;
bool mIsHSTSPriming: 1;
bool mIsHSTSPrimingUpgrade: 1;
};
} // namespace net

View File

@ -675,6 +675,16 @@ interface nsILoadInfo : nsISupports
*/
[noscript, infallible] readonly attribute boolean mixedContentWouldBlock;
/**
* True if this load is an HSTS priming request.
*/
[noscript, infallible] attribute boolean isHSTSPriming;
/**
* True if this load was upgraded from HSTS priming
*/
[noscript, infallible] attribute boolean isHSTSPrimingUpgrade;
/**
* Mark this LoadInfo as needing HSTS Priming
*

View File

@ -2569,6 +2569,14 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
aShouldUpgrade = true;
return NS_OK;
}
if (aLoadInfo->GetForceHSTSPriming()) {
// don't log requests which might be upgraded due to HSTS Priming
// they get logged in nsHttpChannel::OnHSTSPrimingSucceeded or
// nsHttpChannel::OnHSTSPrimingFailed if the load is allowed to proceed.
aShouldUpgrade = false;
return NS_OK;
}
}
// enforce Strict-Transport-Security
@ -2598,6 +2606,21 @@ NS_ShouldSecureUpgrade(nsIURI* aURI,
Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 1);
}
} else {
if (aLoadInfo) {
if (aLoadInfo->GetIsHSTSPriming()) {
// don't log HSTS priming requests
aShouldUpgrade = false;
return NS_OK;
}
if (aLoadInfo->GetIsHSTSPrimingUpgrade()) {
// if the upgrade occured due to HSTS priming, it was logged in
// nsHttpChannel::OnHSTSPrimingSucceeded before redirect
aShouldUpgrade = false;
return NS_OK;
}
}
Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0);
}
aShouldUpgrade = false;

View File

@ -63,6 +63,8 @@ struct LoadInfoArgs
bool isPreflight;
bool forceHSTSPriming;
bool mixedContentWouldBlock;
bool isHSTSPriming;
bool isHSTSPrimingUpgrade;
};
/**

View File

@ -51,21 +51,24 @@ HSTSPrimingListener::GetInterface(const nsIID & aIID, void **aResult)
}
void
HSTSPrimingListener::ReportTiming(nsresult aResult)
HSTSPrimingListener::ReportTiming(nsIHstsPrimingCallback* aCallback, nsresult aResult)
{
nsCOMPtr<nsITimedChannel> timingChannel =
do_QueryInterface(mCallback);
if (timingChannel) {
TimeStamp channelCreationTime;
nsresult rv = timingChannel->GetChannelCreation(&channelCreationTime);
if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
PRUint32 interval =
(PRUint32) (TimeStamp::Now() - channelCreationTime).ToMilliseconds();
Telemetry::Accumulate(Telemetry::HSTS_PRIMING_REQUEST_DURATION,
(NS_SUCCEEDED(aResult)) ? NS_LITERAL_CSTRING("success")
: NS_LITERAL_CSTRING("failure"),
interval);
}
do_QueryInterface(aCallback);
if (!timingChannel) {
LOG(("HSTS priming: mCallback is not an nsITimedChannel!"));
return;
}
TimeStamp channelCreationTime;
nsresult rv = timingChannel->GetChannelCreation(&channelCreationTime);
if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
PRUint32 interval =
(PRUint32) (TimeStamp::Now() - channelCreationTime).ToMilliseconds();
Telemetry::Accumulate(Telemetry::HSTS_PRIMING_REQUEST_DURATION,
(NS_SUCCEEDED(aResult)) ? NS_LITERAL_CSTRING("success")
: NS_LITERAL_CSTRING("failure"),
interval);
}
}
@ -88,7 +91,7 @@ HSTSPrimingListener::OnStartRequest(nsIRequest *aRequest,
}
nsresult primingResult = CheckHSTSPrimingRequestStatus(aRequest);
ReportTiming(primingResult);
ReportTiming(callback, primingResult);
if (NS_FAILED(primingResult)) {
LOG(("HSTS Priming Failed (request was not approved)"));
@ -192,7 +195,7 @@ HSTSPrimingListener::Notify(nsITimer* timer)
return NS_OK;
}
ReportTiming(NS_ERROR_HSTS_PRIMING_TIMEOUT);
ReportTiming(callback, NS_ERROR_HSTS_PRIMING_TIMEOUT);
if (mPrimingChannel) {
rv = mPrimingChannel->Cancel(NS_ERROR_HSTS_PRIMING_TIMEOUT);
@ -237,12 +240,16 @@ HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
if (hsts) {
// already saw this host and will upgrade if allowed by preferences
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_CACHED_HSTS);
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.
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_CACHED_NO_HSTS);
return aCallback->OnHSTSPrimingFailed(NS_ERROR_CONTENT_BLOCKED, true);
}
@ -257,6 +264,7 @@ HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
nsCOMPtr<nsILoadInfo> loadInfo = static_cast<mozilla::LoadInfo*>
(originalLoadInfo.get())->CloneForNewRequest();
loadInfo->SetIsHSTSPriming(true);
// the LoadInfo must have a security flag set in order to pass through priming
// if none of these security flags are set, go ahead and fail now instead of
@ -370,6 +378,9 @@ HSTSPrimingListener::StartHSTSPriming(nsIChannel* aRequestChannel,
listener->mHSTSPrimingTimer.swap(timer);
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_SENT);
return NS_OK;
}

View File

@ -26,6 +26,31 @@ namespace net {
class HttpChannelParent;
class nsHttpChannel;
/*
* How often do we send an HSTS priming request (over all requests)
*/
enum HSTSPrimingRequest {
// No HSTS priming request. The request is not mixed-content, or we have
// already cached the result before nsMixedContentBlocker::ShouldLoad
eHSTS_PRIMING_NO_REQUEST = 0,
// Sent an HSTS priming request
eHSTS_PRIMING_REQUEST_SENT = 1,
// Channel marked for priming, but already had a cached result
eHSTS_PRIMING_REQUEST_CACHED_HSTS = 2,
// Channel marked for priming, but already had a cached result
eHSTS_PRIMING_REQUEST_CACHED_NO_HSTS = 3,
// An error occured setting up the the priming request channel. If the
// priming channel failed in OnstopRequest, there is no HSTS, or the
// channel is redirected, that is recorded by
// MIXED_CONTENT_HSTS_PRIMING_RESULT.
eHSTS_PRIMING_REQUEST_ERROR = 4,
// The channel had no load info, so is ineligible for priming
eHSTS_PRIMING_REQUEST_NO_LOAD_INFO = 5,
// The request was marked for HSTS priming, but was upgraded by
// NS_ShouldSecureUpgrade before HSTS priming was sent.
eHSTS_PRIMING_REQUEST_ALREADY_UPGRADED = 6,
};
/*
* How often do we get back an HSTS priming result which upgrades the connection to HTTPS?
*/
@ -56,7 +81,7 @@ enum HSTSPrimingResult {
eHSTS_PRIMING_TIMEOUT_BLOCK = 9,
// The HSTS Priming request timed out, and the load is allowed by
// mixed-content
eHSTS_PRIMING_TIMEOUT_ACCEPT = 10
eHSTS_PRIMING_TIMEOUT_ACCEPT = 10,
};
//////////////////////////////////////////////////////////////////////////
@ -103,7 +128,7 @@ private:
nsresult CheckHSTSPrimingRequestStatus(nsIRequest* aRequest);
// send telemetry about how long HSTS priming requests take
void ReportTiming(nsresult aResult);
void ReportTiming(nsIHstsPrimingCallback* aCallback, nsresult aResult);
/**
* the nsIHttpChannel to notify with the result of HSTS priming.

View File

@ -483,6 +483,11 @@ nsresult
nsHttpChannel::TryHSTSPriming()
{
if (mLoadInfo) {
if (mLoadInfo->GetIsHSTSPriming()) {
// shortcut priming requests so they don't get counted
return ContinueConnect();
}
// HSTS priming requires the LoadInfo provided with AsyncOpen2
bool requireHSTSPriming =
mLoadInfo->GetForceHSTSPriming();
@ -498,18 +503,38 @@ nsHttpChannel::TryHSTSPriming()
if (NS_FAILED(rv)) {
CloseCacheEntry(false);
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_ERROR);
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);
if (!mLoadInfo->GetIsHSTSPrimingUpgrade()) {
// The request was already upgraded, for example by a prior
// successful priming request
LOG(("HSTS Priming: request already upgraded"));
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
HSTSPrimingResult::eHSTS_PRIMING_ALREADY_UPGRADED);
// No HSTS Priming request was sent.
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_ALREADY_UPGRADED);
}
mLoadInfo->ClearHSTSPriming();
return ContinueConnect();
}
if (!mLoadInfo->GetIsHSTSPrimingUpgrade()) {
// No HSTS Priming request was sent, and we didn't already record this request
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
HSTSPrimingRequest::eHSTS_PRIMING_NO_REQUEST);
}
} else {
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_NO_LOAD_INFO);
}
return ContinueConnect();
@ -8605,6 +8630,14 @@ nsHttpChannel::OnPreflightFailed(nsresult aError)
nsresult
nsHttpChannel::OnHSTSPrimingSucceeded(bool aCached)
{
// 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();
// Clear out the HSTS priming flags on the LoadInfo to simplify the logic in
// TryHSTSPriming()
mLoadInfo->ClearHSTSPriming();
if (nsMixedContentBlocker::sUseHSTS) {
// redirect the channel to HTTPS if the pref
// "security.mixed_content.use_hsts" is true
@ -8612,14 +8645,12 @@ 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
Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3);
mLoadInfo->SetIsHSTSPrimingUpgrade(true);
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]",
@ -8634,6 +8665,9 @@ nsHttpChannel::OnHSTSPrimingSucceeded(bool aCached)
Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_HTTP);
// log HTTP_SCHEME_UPGRADE telemetry
Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0);
nsresult rv = ContinueConnect();
if (NS_FAILED(rv)) {
CloseCacheEntry(false);
@ -8651,6 +8685,9 @@ nsresult
nsHttpChannel::OnHSTSPrimingFailed(nsresult aError, bool aCached)
{
bool wouldBlock = mLoadInfo->GetMixedContentWouldBlock();
// Clear out the HSTS priming flags on the LoadInfo to simplify the logic in
// TryHSTSPriming()
mLoadInfo->ClearHSTSPriming();
LOG(("HSTS Priming Failed [this=%p], %s the load", this,
(wouldBlock) ? "blocking" : "allowing"));
@ -8692,6 +8729,9 @@ nsHttpChannel::OnHSTSPrimingFailed(nsresult aError, bool aCached)
return AsyncAbort(aError);
}
// log HTTP_SCHEME_UPGRADE telemetry
Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0);
// we can continue the load and the UI has been updated as mixed content
rv = ContinueConnect();
if (NS_FAILED(rv)) {

View File

@ -10107,35 +10107,44 @@
},
"MIXED_CONTENT_HSTS": {
"record_in_processes": ["main", "content"],
"alert_emails": ["seceng@mozilla.org"],
"alert_emails": ["seceng-telemetry@mozilla.com"],
"expires_in_version": "never",
"kind": "enumerated",
"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": {
"record_in_processes": ["main", "content"],
"alert_emails": ["seceng@mozilla.org"],
"bug_numbers": [1246540],
"MIXED_CONTENT_HSTS_PRIMING_2": {
"record_in_processes": [ "main" ],
"alert_emails": ["seceng-telemetry@mozilla.com"],
"bug_numbers": [1359987],
"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"
"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=passive/HSTS, 1=active/HSTS, 2=passive/no priming, 3=passive/priming, 4=active/no priming, 5=active/priming, 6=passive/will HSTS upgrade, 7=active/will HSTS upgrade"
},
"MIXED_CONTENT_HSTS_PRIMING_RESULT": {
"record_in_processes": ["main", "content"],
"alert_emails": ["seceng@mozilla.org"],
"record_in_processes": [ "main" ],
"alert_emails": ["seceng-telemetry@mozilla.com"],
"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), 9=timeout (block), 10=timeout (accept)"
},
"MIXED_CONTENT_HSTS_PRIMING_REQUESTS": {
"record_in_processes": [ "main" ],
"alert_emails": ["seceng-telemetry@mozilla.com"],
"bug_numbers": [1359987],
"expires_in_version": "62",
"kind": "enumerated",
"n_values": 10,
"description": "How often does a request result in HSTS priming? (0=Sent HSTS priming, 1=No priming, 2=Priming skipped due to cached HSTS, 3=Priming skipped due to cached NO HSTS, 4=Priming failed (request error), 5=Priming skipped (missing load info), 6=Priming skipped (already upgraded)"
},
"HSTS_PRIMING_REQUEST_DURATION": {
"record_in_processes": ["main", "content"],
"alert_emails": ["seceng-telemetry@mozilla.org"],
"bug_numbers": [1311893],
"expires_in_version": "58",
"record_in_processes": [ "main" ],
"alert_emails": ["seceng-telemetry@mozilla.com"],
"bug_numbers": [1311893, 1359987],
"expires_in_version": "62",
"kind": "exponential",
"low": 100,
"high": 30000,