bug 1412438 - add preference to disable HPKP by default r=jcj

As Chrome has removed support for the HPKP (HTTP Public Key Pinning) header,
continuing to support it in Firefox is a compatibility risk. This patch adds
the preference "security.cert_pinning.hpkp.enabled" and sets it to false by
default. As such, the platform will no longer process the HPKP header nor
consult any cached HPKP information for certificate pins.
Preloaded (statically-compiled) pins are still enabled in Firefox by default.
This patch also disables dynamically setting pins via our remote security
settings infrastructure, as it uses the same backend and represents similar
compatibility risk.

Differential Revision: https://phabricator.services.mozilla.com/D52773

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Dana Keeler 2019-11-13 18:35:35 +00:00
parent ad56e58826
commit a841102f18
17 changed files with 116 additions and 15 deletions

View File

@ -26,9 +26,10 @@ const gSSService = Cc["@mozilla.org/ssservice;1"].getService(
);
const kPinningDomain = "include-subdomains.pinning-dynamic.example.com";
const khpkpPinninEnablePref =
const kHPKPEnabledPref = "security.cert_pinning.hpkp.enabled";
const kProcessHPKPFromNonBuiltInRootsPref =
"security.cert_pinning.process_headers_from_non_builtin_roots";
const kpkpEnforcementPref = "security.cert_pinning.enforcement_level";
const kPinningEnforcementPref = "security.cert_pinning.enforcement_level";
const kBadPinningDomain = "bad.include-subdomains.pinning-dynamic.example.com";
const kURLPath =
"/browser/browser/base/content/test/general/pinning_headers.sjs?";
@ -37,11 +38,13 @@ function test() {
waitForExplicitFinish();
// Enable enforcing strict pinning and processing headers from
// non-builtin roots.
Services.prefs.setIntPref(kpkpEnforcementPref, 2);
Services.prefs.setBoolPref(khpkpPinninEnablePref, true);
Services.prefs.setIntPref(kPinningEnforcementPref, 2);
Services.prefs.setBoolPref(kHPKPEnabledPref, true);
Services.prefs.setBoolPref(kProcessHPKPFromNonBuiltInRootsPref, true);
registerCleanupFunction(function() {
Services.prefs.clearUserPref(kpkpEnforcementPref);
Services.prefs.clearUserPref(khpkpPinninEnablePref);
Services.prefs.clearUserPref(kPinningEnforcementPref);
Services.prefs.clearUserPref(kHPKPEnabledPref);
Services.prefs.clearUserPref(kProcessHPKPFromNonBuiltInRootsPref);
let uri = Services.io.newURI("https://" + kPinningDomain);
gSSService.resetState(Ci.nsISiteSecurityService.HEADER_HPKP, uri, 0);
});

View File

@ -14,14 +14,18 @@ const LEARN_MORE_URI =
"https://developer.mozilla.org/docs/Web/HTTP/" +
"Public_Key_Pinning" +
DOCS_GA_PARAMS;
const HPKP_ENABLED_PREF = "security.cert_pinning.hpkp.enabled";
const NON_BUILTIN_ROOT_PREF =
"security.cert_pinning.process_headers_from_non_builtin_roots";
add_task(async function() {
registerCleanupFunction(() => {
Services.prefs.clearUserPref(HPKP_ENABLED_PREF);
Services.prefs.clearUserPref(NON_BUILTIN_ROOT_PREF);
});
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, true);
const hud = await openNewTabAndConsole(TEST_URI);
await navigateAndCheckForWarningMessage(

View File

@ -17,7 +17,8 @@
SimpleTest.waitForExplicitFinish();
let gCurrentTestCase = -1;
const HPKP_PREF = "security.cert_pinning.process_headers_from_non_builtin_roots";
const HPKP_ENABLED_PREF = "security.cert_pinning.hpkp.enabled";
const PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF = "security.cert_pinning.process_headers_from_non_builtin_roots";
// Static pins tested by unit/test_security-info-static-hpkp.js.
const TEST_CASES = [
@ -40,11 +41,11 @@ const TEST_CASES = [
];
function startTest() {
// Need to enable this pref or pinning headers are rejected due test
// certificate.
Services.prefs.setBoolPref(HPKP_PREF, true);
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, true);
Services.prefs.setBoolPref(PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF, true);
SimpleTest.registerCleanupFunction(() => {
Services.prefs.setBoolPref(HPKP_PREF, false);
Services.prefs.setBoolPref(HPKP_ENABLED_PREF, false);
Services.prefs.setBoolPref(PROCESS_HPKP_FROM_NON_BUILTIN_ROOTS_PREF, false);
// Reset pinning state.
let gSSService = Cc["@mozilla.org/ssservice;1"]

View File

@ -2259,6 +2259,15 @@ pref("security.cert_pinning.enforcement_level", 0);
// for tests.
pref("security.cert_pinning.process_headers_from_non_builtin_roots", false);
// Controls whether or not HPKP (the HTTP Public Key Pinning header) is enabled.
// If true, the header is processed and collected HPKP information is consulted
// when looking for pinning information.
// If false, the header is not processed and collected HPKP information is not
// consulted when looking for pinning information. Preloaded pins are not
// affected by this preference.
// Default: false
pref("security.cert_pinning.hpkp.enabled", false);
// If set to true strict checks will happen on the triggering principal for loads.
// Android is disabled at the moment pending Bug 1504968
#if !defined(RELEASE_OR_BETA) && !defined(ANDROID)

View File

@ -455,6 +455,7 @@ nsSiteSecurityService::nsSiteSecurityService()
: mMaxMaxAge(kSixtyDaysInSeconds),
mUsePreloadList(true),
mPreloadListTimeOffset(0),
mHPKPEnabled(false),
mProcessPKPHeadersFromNonBuiltInRoots(false),
mDafsa(kDafsa) {}
@ -477,6 +478,10 @@ nsresult nsSiteSecurityService::Init() {
"network.stricttransportsecurity.preloadlist", true);
mozilla::Preferences::AddStrongObserver(
this, "network.stricttransportsecurity.preloadlist");
mHPKPEnabled = mozilla::Preferences::GetBool(
"security.cert_pinning.hpkp.enabled", false);
mozilla::Preferences::AddStrongObserver(this,
"security.cert_pinning.hpkp.enabled");
mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
"security.cert_pinning.process_headers_from_non_builtin_roots", false);
mozilla::Preferences::AddStrongObserver(
@ -994,6 +999,17 @@ nsresult nsSiteSecurityService::ProcessPKPHeader(
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
}
if (!mHPKPEnabled) {
SSSLOG(("SSS: HPKP disabled: not processing header '%s'", aHeader.get()));
if (aMaxAge) {
*aMaxAge = 0;
}
if (aIncludeSubdomains) {
*aIncludeSubdomains = false;
}
return NS_OK;
}
SSSLOG(("SSS: processing HPKP header '%s'", aHeader.get()));
NS_ENSURE_ARG(aSecInfo);
@ -1587,7 +1603,7 @@ nsSiteSecurityService::GetKeyPinsForHostname(
const OriginAttributes& aOriginAttributes,
/*out*/ nsTArray<nsCString>& pinArray,
/*out*/ bool* aIncludeSubdomains,
/*out*/ bool* afound) {
/*out*/ bool* aFound) {
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
MOZ_CRASH(
@ -1595,11 +1611,18 @@ nsSiteSecurityService::GetKeyPinsForHostname(
"nsISiteSecurityService::GetKeyPinsForHostname");
}
NS_ENSURE_ARG(afound);
NS_ENSURE_ARG(aFound);
const nsCString& flatHostname = PromiseFlatCString(aHostname);
if (!mHPKPEnabled) {
SSSLOG(("HPKP disabled - returning 'pins not found' for %s",
flatHostname.get()));
*aFound = false;
return NS_OK;
}
SSSLOG(("Top of GetKeyPinsForHostname for %s", flatHostname.get()));
*afound = false;
*aFound = false;
*aIncludeSubdomains = false;
pinArray.Clear();
@ -1640,7 +1663,7 @@ nsSiteSecurityService::GetKeyPinsForHostname(
}
pinArray = foundEntry->mSHA256keys;
*aIncludeSubdomains = foundEntry->mIncludeSubdomains;
*afound = true;
*aFound = true;
return NS_OK;
}
@ -1660,6 +1683,13 @@ nsSiteSecurityService::SetKeyPins(const nsACString& aHost,
}
NS_ENSURE_ARG_POINTER(aResult);
if (!mHPKPEnabled) {
SSSLOG(("SSS: HPKP disabled: not setting pins"));
*aResult = false;
return NS_OK;
}
OriginAttributes originAttributes;
if (aArgc > 1) {
// OriginAttributes were passed in.
@ -1815,6 +1845,8 @@ nsSiteSecurityService::Observe(nsISupports* /*subject*/, const char* topic,
"network.stricttransportsecurity.preloadlist", true);
mPreloadListTimeOffset =
mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0);
mHPKPEnabled = mozilla::Preferences::GetBool(
"security.cert_pinning.hpkp.enabled", false);
mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
"security.cert_pinning.process_headers_from_non_builtin_roots", false);
mMaxMaxAge = mozilla::Preferences::GetInt(

View File

@ -213,6 +213,7 @@ class nsSiteSecurityService : public nsISiteSecurityService,
uint64_t mMaxMaxAge;
bool mUsePreloadList;
int64_t mPreloadListTimeOffset;
bool mHPKPEnabled;
bool mProcessPKPHeadersFromNonBuiltInRoots;
RefPtr<mozilla::DataStorage> mSiteStateStorage;
RefPtr<mozilla::DataStorage> mPreloadStateStorage;

View File

@ -14,6 +14,8 @@ const sss = Cc["@mozilla.org/ssservice;1"].getService(
const { PinningBlocklistClient } = RemoteSecuritySettings.init();
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
add_task(async function test_uses_a_custom_signer() {
Assert.notEqual(
PinningBlocklistClient.signerName,

View File

@ -16,6 +16,7 @@ const { ForgetAboutSite } = ChromeUtils.import(
do_get_profile(); // must be done before instantiating nsIX509CertDB
registerCleanupFunction(() => {
Services.prefs.clearUserPref("security.cert_pinning.hpkp.enabled");
Services.prefs.clearUserPref("security.cert_pinning.enforcement_level");
Services.prefs.clearUserPref(
"security.cert_pinning.process_headers_from_non_builtin_roots"
@ -46,6 +47,26 @@ function add_tests() {
}
);
// Test that with HPKP disabled, processing HPKP headers results in no
// information being saved.
add_task(async function() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", false);
sss.processHeader(
Ci.nsISiteSecurityService.HEADER_HPKP,
uri,
GOOD_MAX_AGE + VALID_PIN + BACKUP_PIN,
secInfo,
0,
Ci.nsISiteSecurityService.SOURCE_ORGANIC_REQUEST
);
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Assert.ok(
!sss.isSecureURI(Ci.nsISiteSecurityService.HEADER_HPKP, uri, 0),
"a.pinning.example.com should not be HPKP"
);
});
// Test the normal case of processing HSTS and HPKP headers for
// a.pinning.example.com, using "Forget About Site" on a.pinning2.example.com,
// and then checking that the platform doesn't consider a.pinning.example.com
@ -266,6 +287,7 @@ function add_tests() {
}
function run_test() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
Services.prefs.setBoolPref(
"security.cert_pinning.process_headers_from_non_builtin_roots",

View File

@ -42,6 +42,7 @@ function add_tests() {
true,
false,
function(aSecInfo) {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1);
Services.prefs.setBoolPref(
"security.cert_pinning.process_headers_from_non_builtin_roots",
@ -74,6 +75,7 @@ function add_tests() {
Services.prefs.clearUserPref(
"security.cert_pinning.process_headers_from_non_builtin_roots"
);
Services.prefs.clearUserPref("security.cert_pinning.hpkp.enabled");
Services.prefs.clearUserPref("security.cert_pinning.enforcement_level");
}
);

View File

@ -299,6 +299,9 @@ function check_pinning_telemetry() {
}
function run_test() {
// Ensure that static pinning works when HPKP is disabled.
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", false);
add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
// Add a user-specified trust anchor.

View File

@ -49,6 +49,7 @@ const NON_ISSUED_KEY_HASH = "KHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN=";
const PINNING_ROOT_KEY_HASH = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
function run_test() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
let stateFile = profileDir.clone();
@ -145,6 +146,7 @@ function checkStateRead(aSubject, aTopic, aData) {
}
async_check_pins()
.then(checkHPKPDisabled)
.then(function() {
return new Promise((resolve, reject) => {
do_timeout(1250, resolve);
@ -411,6 +413,16 @@ async function async_check_pins() {
);
}
// Check that if HPKP is disabled, accumulated HPKP information isn't consulted.
async function checkHPKPDisabled() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", false);
await checkOK(
certFromFile("a.pinning2.example.com-badca"),
"a.pinning2.example.com"
);
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
}
async function checkExpiredState() {
await checkOK(
certFromFile("a.pinning2.example.com-badca"),

View File

@ -201,11 +201,13 @@ function add_tests() {
}
registerCleanupFunction(() => {
Services.prefs.clearUserPref("security.cert_pinning.hpkp.enabled");
Services.prefs.clearUserPref("security.cert_pinning.enforcement_level");
Services.prefs.clearUserPref("security.cert_pinning.max_max_age_seconds");
});
function run_test() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
Services.prefs.setIntPref(
"security.cert_pinning.max_max_age_seconds",

View File

@ -73,6 +73,7 @@ function checkSha256Keys(hpkpEntries) {
}
registerCleanupFunction(() => {
Services.prefs.clearUserPref("security.cert_pinning.hpkp.enabled");
Services.prefs.clearUserPref(
"security.cert_pinning.process_headers_from_non_builtin_roots"
);
@ -138,6 +139,7 @@ function add_tests() {
}
function run_test() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Services.prefs.setBoolPref(
"security.cert_pinning.process_headers_from_non_builtin_roots",
true

View File

@ -9,6 +9,7 @@
// Ensures nsISiteSecurityService APIs respects origin attributes.
registerCleanupFunction(() => {
Services.prefs.clearUserPref("security.cert_pinning.hpkp.enabled");
Services.prefs.clearUserPref("security.cert_pinning.enforcement_level");
Services.prefs.clearUserPref(
"security.cert_pinning.process_headers_from_non_builtin_roots"
@ -24,6 +25,7 @@ const GOOD_MAX_AGE = `max-age=${GOOD_MAX_AGE_SECONDS};`;
do_get_profile(); // must be done before instantiating nsIX509CertDB
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
Services.prefs.setBoolPref(
"security.cert_pinning.process_headers_from_non_builtin_roots",

View File

@ -109,6 +109,7 @@ const MULTIPLE_KEYS =
PINNING_ROOT_KEY_HASH + STARTS_WITH_NUMBER + STARTS_WITH_SYMBOL;
function run_test() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
let profileDir = do_get_profile();
let stateFile = profileDir.clone();
stateFile.append(SSS_STATE_FILE_NAME);

View File

@ -129,12 +129,14 @@ function add_tests() {
}
registerCleanupFunction(() => {
Services.prefs.clearUserPref("security.cert_pinning.hpkp.enabled");
Services.prefs.clearUserPref(
"sercurity.cert_pinning.process_headers_from_non_builtin_roots"
);
});
function run_test() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Services.prefs.setBoolPref(
"security.cert_pinning.process_headers_from_non_builtin_roots",
true

View File

@ -94,6 +94,7 @@ function checkStateWritten(aSubject, aTopic, aData) {
}
function run_test() {
Services.prefs.setBoolPref("security.cert_pinning.hpkp.enabled", true);
Services.prefs.setIntPref("test.datastorage.write_timer_ms", 100);
gProfileDir = do_get_profile();
let SSService = Cc["@mozilla.org/ssservice;1"].getService(