bug 1412438 - add preference to disable HPKP by default r=jcj
authorDana Keeler <dkeeler@mozilla.com>
Wed, 13 Nov 2019 18:35:35 +0000
changeset 501813 d791bfa31f08ec478b2ef6ca4f89b3a8849d723b
parent 501812 c17276cc50c4d18a28a8653028f9489ba998d9bc
child 501814 aa9475c27a6d5c4359f3a6d1c058ef9b1b440f5b
push id114172
push userdluca@mozilla.com
push dateTue, 19 Nov 2019 11:31:10 +0000
treeherdermozilla-inbound@b5c5ba07d3db [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjcj
bugs1412438
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
browser/base/content/test/general/browser_blockHPKP.js
devtools/client/webconsole/test/browser/browser_webconsole_hpkp_invalid-headers.js
devtools/shared/webconsole/test/test_network_security-hpkp.html
modules/libpref/init/all.js
security/manager/ssl/nsSiteSecurityService.cpp
security/manager/ssl/nsSiteSecurityService.h
security/manager/ssl/tests/unit/test_blocklist_pinning.js
security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js
security/manager/ssl/tests/unit/test_ocsp_must_staple.js
security/manager/ssl/tests/unit/test_pinning.js
security/manager/ssl/tests/unit/test_pinning_dynamic.js
security/manager/ssl/tests/unit/test_pinning_header_parsing.js
security/manager/ssl/tests/unit/test_sss_enumerate.js
security/manager/ssl/tests/unit/test_sss_originAttributes.js
security/manager/ssl/tests/unit/test_sss_readstate_garbage.js
security/manager/ssl/tests/unit/test_sss_resetState.js
security/manager/ssl/tests/unit/test_sss_savestate.js
--- a/browser/base/content/test/general/browser_blockHPKP.js
+++ b/browser/base/content/test/general/browser_blockHPKP.js
@@ -21,32 +21,35 @@
 // 3. A certificate with a different issuer, so as to cause a key pinning violation."
 //   certutil -S -n "dynamicPinningBad" -s "CN=bad.include-subdomains.pinning-dynamic.example.com" -c "alternateTrustedAuthority" -t "P,," -k rsa -g 2048 -Z SHA256 -m 893945439 -v 120 -8 "bad.include-subdomains.pinning-dynamic.example.com" -d .
 
 const gSSService = Cc["@mozilla.org/ssservice;1"].getService(
   Ci.nsISiteSecurityService
 );
 
 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?";
 
 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);
   });
   whenNewTabLoaded(window, loadPinningPage);
 }
 
 // Start by making a successful connection to a domain that will pin a site
 function loadPinningPage() {
--- a/devtools/client/webconsole/test/browser/browser_webconsole_hpkp_invalid-headers.js
+++ b/devtools/client/webconsole/test/browser/browser_webconsole_hpkp_invalid-headers.js
@@ -9,24 +9,28 @@ const TEST_URI =
   "data:text/html;charset=utf-8,Web Console HPKP invalid header test";
 const SJS_URL =
   "https://example.com/browser/devtools/client/webconsole/" +
   "test/browser/test_hpkp-invalid-headers.sjs";
 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(
     {
       url: SJS_URL + "?badSyntax",
       name: "Could not parse header error displayed successfully",
       text:
         "Public-Key-Pins: The site specified a header that could not be " +
--- a/devtools/shared/webconsole/test/test_network_security-hpkp.html
+++ b/devtools/shared/webconsole/test/test_network_security-hpkp.html
@@ -12,17 +12,18 @@
 <p>Test for the network actor (HPKP detection)</p>
 
 <iframe src="https://example.com/chrome/devtools/shared/webconsole/test/network_requests_iframe.html"></iframe>
 
 <script class="testbody" type="text/javascript">
 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 = [
   {
     desc: "no Public Key Pinning",
     url: "https://example.com",
     usesPinning: false,
   },
@@ -35,21 +36,21 @@ const TEST_CASES = [
   {
     desc: "dynamic Public Key Pinning with previous request",
     url: "https://include-subdomains.pinning-dynamic.example.com/",
     usesPinning: true,
   }
 ];
 
 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"]
                        .getService(Ci.nsISiteSecurityService);
 
     let gIOService = Cc["@mozilla.org/network/io-service;1"]
                        .getService(Ci.nsIIOService);
     for (let {url} of TEST_CASES) {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2254,16 +2254,25 @@ pref("security.insecure_field_warning.ig
 
 // Disable pinning checks by default.
 pref("security.cert_pinning.enforcement_level", 0);
 // Do not process hpkp headers rooted by not built in roots by default.
 // This is to prevent accidental pinning from MITM devices and is used
 // 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)
   pref("security.strict_security_checks.enabled", true);
 #else
   pref("security.strict_security_checks.enabled", false);
 #endif
 
--- a/security/manager/ssl/nsSiteSecurityService.cpp
+++ b/security/manager/ssl/nsSiteSecurityService.cpp
@@ -450,16 +450,17 @@ SiteHPKPState::GetOriginAttributes(
 ////////////////////////////////////////////////////////////////////////////////
 
 const uint64_t kSixtyDaysInSeconds = 60 * 24 * 60 * 60;
 
 nsSiteSecurityService::nsSiteSecurityService()
     : mMaxMaxAge(kSixtyDaysInSeconds),
       mUsePreloadList(true),
       mPreloadListTimeOffset(0),
+      mHPKPEnabled(false),
       mProcessPKPHeadersFromNonBuiltInRoots(false),
       mDafsa(kDafsa) {}
 
 nsSiteSecurityService::~nsSiteSecurityService() {}
 
 NS_IMPL_ISUPPORTS(nsSiteSecurityService, nsIObserver, nsISiteSecurityService)
 
 nsresult nsSiteSecurityService::Init() {
@@ -472,16 +473,20 @@ nsresult nsSiteSecurityService::Init() {
   mMaxMaxAge = mozilla::Preferences::GetInt(
       "security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
   mozilla::Preferences::AddStrongObserver(
       this, "security.cert_pinning.max_max_age_seconds");
   mUsePreloadList = mozilla::Preferences::GetBool(
       "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(
       this, "security.cert_pinning.process_headers_from_non_builtin_roots");
   mPreloadListTimeOffset =
       mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0);
   mozilla::Preferences::AddStrongObserver(this,
                                           "test.currentTimeOffsetSeconds");
@@ -989,16 +994,27 @@ static uint32_t ParseSSSHeaders(uint32_t
 nsresult nsSiteSecurityService::ProcessPKPHeader(
     nsIURI* aSourceURI, const nsCString& aHeader,
     nsITransportSecurityInfo* aSecInfo, uint32_t aFlags,
     const OriginAttributes& aOriginAttributes, uint64_t* aMaxAge,
     bool* aIncludeSubdomains, uint32_t* aFailureResult) {
   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);
 
   const uint32_t aType = nsISiteSecurityService::HEADER_HPKP;
   bool foundMaxAge = false;
   bool foundIncludeSubdomains = false;
   bool foundUnrecognizedDirective = false;
   uint64_t maxAge = 0;
@@ -1582,29 +1598,36 @@ bool entryStateNotOK(SiteHPKPState& stat
 }
 
 NS_IMETHODIMP
 nsSiteSecurityService::GetKeyPinsForHostname(
     const nsACString& aHostname, mozilla::pkix::Time& aEvalTime,
     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(
         "Child process: no direct access to "
         "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();
 
   nsAutoCString host(
       PublicKeyPinningService::CanonicalizeHostname(flatHostname.get()));
   nsAutoCString storageKey;
   SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP, aOriginAttributes,
                 storageKey);
@@ -1635,17 +1658,17 @@ nsSiteSecurityService::GetKeyPinsForHost
       }
       foundEntry = preloadEntry;
     } else {
       foundEntry = privateEntry;
     }
   }
   pinArray = foundEntry->mSHA256keys;
   *aIncludeSubdomains = foundEntry->mIncludeSubdomains;
-  *afound = true;
+  *aFound = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSiteSecurityService::SetKeyPins(const nsACString& aHost,
                                   bool aIncludeSubdomains, int64_t aExpires,
                                   const nsTArray<nsCString>& aSha256Pins,
                                   bool aIsPreload,
@@ -1655,16 +1678,23 @@ nsSiteSecurityService::SetKeyPins(const 
   // Child processes are not allowed direct access to this.
   if (!XRE_IsParentProcess()) {
     MOZ_CRASH(
         "Child process: no direct access to "
         "nsISiteSecurityService::SetKeyPins");
   }
 
   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.
     if (!aOriginAttributes.isObject() ||
         !originAttributes.Init(aCx, aOriginAttributes)) {
       return NS_ERROR_INVALID_ARG;
     }
   }
@@ -1810,16 +1840,18 @@ nsSiteSecurityService::Observe(nsISuppor
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   if (strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) == 0) {
     mUsePreloadList = mozilla::Preferences::GetBool(
         "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(
         "security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
   }
 
   return NS_OK;
 }
--- a/security/manager/ssl/nsSiteSecurityService.h
+++ b/security/manager/ssl/nsSiteSecurityService.h
@@ -208,15 +208,16 @@ class nsSiteSecurityService : public nsI
                         uint32_t aFlags,
                         const OriginAttributes& aOriginAttributes,
                         bool* aCached, SecurityPropertySource* aSource,
                         bool* aResult);
 
   uint64_t mMaxMaxAge;
   bool mUsePreloadList;
   int64_t mPreloadListTimeOffset;
+  bool mHPKPEnabled;
   bool mProcessPKPHeadersFromNonBuiltInRoots;
   RefPtr<mozilla::DataStorage> mSiteStateStorage;
   RefPtr<mozilla::DataStorage> mPreloadStateStorage;
   const mozilla::Dafsa mDafsa;
 };
 
 #endif  // __nsSiteSecurityService_h__
--- a/security/manager/ssl/tests/unit/test_blocklist_pinning.js
+++ b/security/manager/ssl/tests/unit/test_blocklist_pinning.js
@@ -9,16 +9,18 @@ const { RemoteSecuritySettings } = Chrom
 );
 
 const sss = Cc["@mozilla.org/ssservice;1"].getService(
   Ci.nsISiteSecurityService
 );
 
 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,
     RemoteSettings("not-specified").signerName
   );
 });
 
 add_task(async function test_pinning_has_initial_dump() {
--- a/security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js
+++ b/security/manager/ssl/tests/unit/test_forget_about_site_security_headers.js
@@ -11,16 +11,17 @@
 
 const { ForgetAboutSite } = ChromeUtils.import(
   "resource://gre/modules/ForgetAboutSite.jsm"
 );
 
 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"
   );
 });
 
 const GOOD_MAX_AGE_SECONDS = 69403;
 const NON_ISSUED_KEY_HASH = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
@@ -41,16 +42,36 @@ function add_tests() {
     "a.pinning.example.com",
     PRErrorCodeSuccess,
     undefined,
     aSecInfo => {
       secInfo = aSecInfo;
     }
   );
 
+  // 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
   // to be HSTS or HPKP any longer.
   add_task(async function() {
     sss.processHeader(
       Ci.nsISiteSecurityService.HEADER_HSTS,
       uri,
@@ -261,16 +282,17 @@ function add_tests() {
         ),
         "example.org should still be HSTS (originAttributes case)"
       );
     }
   });
 }
 
 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",
     true
   );
 
   add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
 
--- a/security/manager/ssl/tests/unit/test_ocsp_must_staple.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_must_staple.js
@@ -37,16 +37,17 @@ function add_tests() {
   // ensure that the chain is checked for required features in children:
   // First a case where intermediate and ee both have the extension
   add_ocsp_test(
     "ocsp-stapling-must-staple-ee-with-must-staple-int.example.com",
     PRErrorCodeSuccess,
     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",
         true
       );
       let uri = Services.io.newURI(
         "https://ocsp-stapling-must-staple-ee-with-must-staple-int.example.com"
       );
@@ -69,16 +70,17 @@ function add_tests() {
         "ocsp-stapling-must-staple-ee-with-must-staple-int.example.com should have HPKP set"
       );
 
       // Clear accumulated state.
       ssservice.resetState(Ci.nsISiteSecurityService.HEADER_HPKP, uri, 0);
       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");
     }
   );
 
   // Next, a case where it's present in the intermediate, not the ee
   add_ocsp_test(
     "ocsp-stapling-plain-ee-with-must-staple-int.example.com",
     MOZILLA_PKIX_ERROR_REQUIRED_TLS_FEATURE_MISSING,
--- a/security/manager/ssl/tests/unit/test_pinning.js
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -294,16 +294,19 @@ function check_pinning_telemetry() {
     0,
     "Actual and expected test (non-Mozilla) success count should match"
   );
 
   run_next_test();
 }
 
 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.
   addCertFromFile(certdb, "bad_certs/other-test-ca.pem", "CTu,u,u");
 
   test_strict();
   test_mitm();
   test_disabled();
--- a/security/manager/ssl/tests/unit/test_pinning_dynamic.js
+++ b/security/manager/ssl/tests/unit/test_pinning_dynamic.js
@@ -44,16 +44,17 @@ function checkFail(cert, hostname) {
     hostname
   );
 }
 
 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();
   stateFile.append(SSS_STATE_FILE_NAME);
   // Assuming we're working with a clean slate, the SSS_STATE file shouldn't
   // exist until we create it.
   ok(
     !stateFile.exists(),
@@ -140,16 +141,17 @@ function checkStateRead(aSubject, aTopic
     );
   }
 
   if (!gSSSStateSeen || !gPreloadStateSeen) {
     return;
   }
 
   async_check_pins()
+    .then(checkHPKPDisabled)
     .then(function() {
       return new Promise((resolve, reject) => {
         do_timeout(1250, resolve);
       });
     })
     .then(checkExpiredState)
     .then(checkPreloadClear)
     .then(do_test_finished);
@@ -406,16 +408,26 @@ async function async_check_pins() {
     true
   );
   await checkFail(
     certFromFile("b.preload.example.com-badca"),
     "b.preload.example.com"
   );
 }
 
+// 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"),
     "a.pinning2.example.com"
   );
   await checkOK(
     certFromFile("a.pinning2.example.com-pinningroot"),
     "a.pinning2.example.com"
--- a/security/manager/ssl/tests/unit/test_pinning_header_parsing.js
+++ b/security/manager/ssl/tests/unit/test_pinning_header_parsing.js
@@ -196,21 +196,23 @@ function add_tests() {
     checkPassSettingPin(
       secInfo,
       GOOD_MAX_AGE + VALID_PIN1 + BACKUP_PIN1 + UNRECOGNIZED_DIRECTIVE
     );
   });
 }
 
 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",
     MAX_MAX_AGE_SECONDS
   );
 
   Services.prefs.setBoolPref(
     "security.cert_pinning.process_headers_from_non_builtin_roots",
--- a/security/manager/ssl/tests/unit/test_sss_enumerate.js
+++ b/security/manager/ssl/tests/unit/test_sss_enumerate.js
@@ -68,16 +68,17 @@ function checkSha256Keys(hpkpEntries) {
     keys.sort();
     for (let i = 0; i < KEY_HASHES.length; i++) {
       equal(keys[i], KEY_HASHES[i], "Should get correct keys");
     }
   }
 }
 
 registerCleanupFunction(() => {
+  Services.prefs.clearUserPref("security.cert_pinning.hpkp.enabled");
   Services.prefs.clearUserPref(
     "security.cert_pinning.process_headers_from_non_builtin_roots"
   );
   Services.prefs.clearUserPref("security.cert_pinning.max_max_age_seconds");
 });
 
 function add_tests() {
   sss.clearAll();
@@ -133,16 +134,17 @@ function add_tests() {
     hpkpEntries = getEntries(Ci.nsISiteSecurityService.HEADER_HPKP);
 
     equal(hstsEntries.length, 0, "Should clear all HSTS entries");
     equal(hpkpEntries.length, 0, "Should clear all HPKP entries");
   });
 }
 
 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
   );
   Services.prefs.setIntPref(
     "security.cert_pinning.max_max_age_seconds",
     20 * SECS_IN_A_WEEK
   );
--- a/security/manager/ssl/tests/unit/test_sss_originAttributes.js
+++ b/security/manager/ssl/tests/unit/test_sss_originAttributes.js
@@ -4,31 +4,33 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // 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"
   );
 });
 
 const GOOD_MAX_AGE_SECONDS = 69403;
 const NON_ISSUED_KEY_HASH = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
 const PINNING_ROOT_KEY_HASH = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
 const VALID_PIN = `pin-sha256="${PINNING_ROOT_KEY_HASH}";`;
 const BACKUP_PIN = `pin-sha256="${NON_ISSUED_KEY_HASH}";`;
 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",
   true
 );
 
 let sss = Cc["@mozilla.org/ssservice;1"].getService(Ci.nsISiteSecurityService);
 let host = "a.pinning.example.com";
--- a/security/manager/ssl/tests/unit/test_sss_readstate_garbage.js
+++ b/security/manager/ssl/tests/unit/test_sss_readstate_garbage.js
@@ -104,16 +104,17 @@ function checkStateRead(aSubject, aTopic
 const PINNING_ROOT_KEY_HASH = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8=";
 const BASE64_BUT_NOT_SHA256 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
 const STARTS_WITH_NUMBER = "1ABC23defG/hiJKlmNoP+QRStuVwxYZ9a+bcD/+/EFg=";
 const STARTS_WITH_SYMBOL = "+ABC23defG/hiJKlmNoP+QRStuVwxYZ9a+bcD/+/EFg=";
 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);
   // Assuming we're working with a clean slate, the file shouldn't exist
   // until we create it.
   ok(!stateFile.exists());
   let outputStream = FileUtils.openFileOutputStream(stateFile);
   let expiryTime = Date.now() + 100000;
--- a/security/manager/ssl/tests/unit/test_sss_resetState.js
+++ b/security/manager/ssl/tests/unit/test_sss_resetState.js
@@ -124,22 +124,24 @@ function add_tests() {
       secInfo,
       Ci.nsISiteSecurityService.HEADER_HPKP,
       Ci.nsISocketProvider.NO_PERMANENT_STORAGE
     );
   });
 }
 
 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
   );
 
   add_tls_server_setup("BadCertAndPinningServer", "bad_certs");
 
   add_tests();
--- a/security/manager/ssl/tests/unit/test_sss_savestate.js
+++ b/security/manager/ssl/tests/unit/test_sss_savestate.js
@@ -89,16 +89,17 @@ function checkStateWritten(aSubject, aTo
     return;
   }
   equal(sites["dynamic-pin.example.com:HPKP"][3], NON_ISSUED_KEY_HASH);
 
   do_test_finished();
 }
 
 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(
     Ci.nsISiteSecurityService
   );
   // Put an HPKP entry
   SSService.setKeyPins(
     "dynamic-pin.example.com",