diff options
Diffstat (limited to 'chromium/net/http/http_security_headers_unittest.cc')
-rw-r--r-- | chromium/net/http/http_security_headers_unittest.cc | 261 |
1 files changed, 225 insertions, 36 deletions
diff --git a/chromium/net/http/http_security_headers_unittest.cc b/chromium/net/http/http_security_headers_unittest.cc index 42a5ee98960..ce919ff81f3 100644 --- a/chromium/net/http/http_security_headers_unittest.cc +++ b/chromium/net/http/http_security_headers_unittest.cc @@ -90,8 +90,6 @@ TEST_F(HttpSecurityHeadersTest, BogusHeaders) { &include_subdomains)); EXPECT_FALSE(ParseHSTSHeader("max-age=-3488923", &max_age, &include_subdomains)); - EXPECT_FALSE(ParseHSTSHeader("max-age=3488923;", &max_age, - &include_subdomains)); EXPECT_FALSE(ParseHSTSHeader("max-age=3488923 e", &max_age, &include_subdomains)); EXPECT_FALSE(ParseHSTSHeader("max-age=3488923 includesubdomain", @@ -114,6 +112,16 @@ TEST_F(HttpSecurityHeadersTest, BogusHeaders) { &max_age, &include_subdomains)); EXPECT_FALSE(ParseHSTSHeader("max-age=34889 includesubdomains", &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader(";;;; ;;;", + &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader(";;;; includeSubDomains;;;", + &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader(" includeSubDomains; ", + &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader(";", + &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader("max-age; ;", + &max_age, &include_subdomains)); // Check the out args were not updated by checking the default // values for its predictable fields. @@ -223,6 +231,9 @@ TEST_F(HttpSecurityHeadersTest, ValidSTSHeaders) { EXPECT_EQ(expect_max_age, max_age); EXPECT_FALSE(include_subdomains); + EXPECT_TRUE(ParseHSTSHeader("max-age=3488923;", &max_age, + &include_subdomains)); + EXPECT_TRUE(ParseHSTSHeader(" Max-agE = 567", &max_age, &include_subdomains)); expect_max_age = base::TimeDelta::FromSeconds(567); @@ -311,6 +322,46 @@ TEST_F(HttpSecurityHeadersTest, ValidSTSHeaders) { EXPECT_TRUE(include_subdomains); EXPECT_TRUE(ParseHSTSHeader( + "max-age=394082038 ; incLudesUbdOmains;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( + ";; max-age=394082038 ; incLudesUbdOmains; ;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( + ";; max-age=394082038 ;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_FALSE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( + ";; ; ; max-age=394082038;;; includeSubdomains ;; ;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( + "incLudesUbdOmains ; max-age=394082038 ;;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( " max-age=0 ; incLudesUbdOmains ", &max_age, &include_subdomains)); expect_max_age = base::TimeDelta::FromSeconds(0); @@ -450,13 +501,15 @@ TEST_F(HttpSecurityHeadersTest, ValidPKPHeadersSHA256) { TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) { TransportSecurityState state; - TransportSecurityState::DomainState domain_state; + TransportSecurityState::DomainState static_domain_state; // docs.google.com has preloaded pins. + const bool sni_enabled = true; std::string domain = "docs.google.com"; - EXPECT_TRUE(state.GetDomainState(domain, true, &domain_state)); - EXPECT_GT(domain_state.static_spki_hashes.size(), 1UL); - HashValueVector saved_hashes = domain_state.static_spki_hashes; + EXPECT_TRUE( + state.GetStaticDomainState(domain, sni_enabled, &static_domain_state)); + EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL); + HashValueVector saved_hashes = static_domain_state.pkp.spki_hashes; // Add a header, which should only update the dynamic state. HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1); @@ -471,49 +524,185 @@ TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) { ssl_info.public_key_hashes.push_back(saved_hashes[0]); EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); - // Expect the preloaded state to remain unchanged. - std::string canonicalized_host = TransportSecurityState::CanonicalizeHost( - domain); - TransportSecurityState::DomainState static_domain_state; - EXPECT_TRUE(state.GetStaticDomainState(canonicalized_host, - true, - &static_domain_state)); + // Expect the static state to remain unchanged. + TransportSecurityState::DomainState new_static_domain_state; + EXPECT_TRUE(state.GetStaticDomainState( + domain, sni_enabled, &new_static_domain_state)); for (size_t i = 0; i < saved_hashes.size(); ++i) { - EXPECT_TRUE(HashValuesEqual( - saved_hashes[i])(static_domain_state.static_spki_hashes[i])); + EXPECT_TRUE(HashValuesEqual(saved_hashes[i])( + new_static_domain_state.pkp.spki_hashes[i])); } // Expect the dynamic state to reflect the header. TransportSecurityState::DomainState dynamic_domain_state; EXPECT_TRUE(state.GetDynamicDomainState(domain, &dynamic_domain_state)); - EXPECT_EQ(2UL, dynamic_domain_state.dynamic_spki_hashes.size()); + EXPECT_EQ(2UL, dynamic_domain_state.pkp.spki_hashes.size()); - HashValueVector::const_iterator hash = std::find_if( - dynamic_domain_state.dynamic_spki_hashes.begin(), - dynamic_domain_state.dynamic_spki_hashes.end(), - HashValuesEqual(good_hash)); - EXPECT_NE(dynamic_domain_state.dynamic_spki_hashes.end(), hash); + HashValueVector::const_iterator hash = + std::find_if(dynamic_domain_state.pkp.spki_hashes.begin(), + dynamic_domain_state.pkp.spki_hashes.end(), + HashValuesEqual(good_hash)); + EXPECT_NE(dynamic_domain_state.pkp.spki_hashes.end(), hash); - hash = std::find_if( - dynamic_domain_state.dynamic_spki_hashes.begin(), - dynamic_domain_state.dynamic_spki_hashes.end(), - HashValuesEqual(backup_hash)); - EXPECT_NE(dynamic_domain_state.dynamic_spki_hashes.end(), hash); + hash = std::find_if(dynamic_domain_state.pkp.spki_hashes.begin(), + dynamic_domain_state.pkp.spki_hashes.end(), + HashValuesEqual(backup_hash)); + EXPECT_NE(dynamic_domain_state.pkp.spki_hashes.end(), hash); // Expect the overall state to reflect the header, too. - EXPECT_TRUE(state.GetDomainState(domain, true, &domain_state)); - EXPECT_EQ(2UL, domain_state.dynamic_spki_hashes.size()); + EXPECT_TRUE(state.HasPublicKeyPins(domain, sni_enabled)); + HashValueVector hashes; + hashes.push_back(good_hash); + std::string failure_log; + EXPECT_TRUE( + state.CheckPublicKeyPins(domain, sni_enabled, hashes, &failure_log)); - hash = std::find_if(domain_state.dynamic_spki_hashes.begin(), - domain_state.dynamic_spki_hashes.end(), + TransportSecurityState::DomainState new_dynamic_domain_state; + EXPECT_TRUE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state)); + EXPECT_EQ(2UL, new_dynamic_domain_state.pkp.spki_hashes.size()); + + hash = std::find_if(new_dynamic_domain_state.pkp.spki_hashes.begin(), + new_dynamic_domain_state.pkp.spki_hashes.end(), HashValuesEqual(good_hash)); - EXPECT_NE(domain_state.dynamic_spki_hashes.end(), hash); + EXPECT_NE(new_dynamic_domain_state.pkp.spki_hashes.end(), hash); + + hash = std::find_if(new_dynamic_domain_state.pkp.spki_hashes.begin(), + new_dynamic_domain_state.pkp.spki_hashes.end(), + HashValuesEqual(backup_hash)); + EXPECT_NE(new_dynamic_domain_state.pkp.spki_hashes.end(), hash); +} + +// Failing on win_chromium_rel. crbug.com/375538 +#if defined(OS_WIN) +#define MAYBE_UpdateDynamicPKPMaxAge0 DISABLED_UpdateDynamicPKPMaxAge0 +#else +#define MAYBE_UpdateDynamicPKPMaxAge0 UpdateDynamicPKPMaxAge0 +#endif +TEST_F(HttpSecurityHeadersTest, MAYBE_UpdateDynamicPKPMaxAge0) { + TransportSecurityState state; + TransportSecurityState::DomainState static_domain_state; + + // docs.google.com has preloaded pins. + const bool sni_enabled = true; + std::string domain = "docs.google.com"; + ASSERT_TRUE( + state.GetStaticDomainState(domain, sni_enabled, &static_domain_state)); + EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL); + HashValueVector saved_hashes = static_domain_state.pkp.spki_hashes; + + // Add a header, which should only update the dynamic state. + HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1); + std::string good_pin = GetTestPin(1, HASH_VALUE_SHA1); + std::string backup_pin = GetTestPin(2, HASH_VALUE_SHA1); + std::string header = "max-age = 10000; " + good_pin + "; " + backup_pin; + + // Construct a fake SSLInfo that will pass AddHPKPHeader's checks. + SSLInfo ssl_info; + ssl_info.public_key_hashes.push_back(good_hash); + ssl_info.public_key_hashes.push_back(saved_hashes[0]); + EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); + + // Expect the static state to remain unchanged. + TransportSecurityState::DomainState new_static_domain_state; + EXPECT_TRUE(state.GetStaticDomainState( + domain, sni_enabled, &new_static_domain_state)); + EXPECT_EQ(saved_hashes.size(), + new_static_domain_state.pkp.spki_hashes.size()); + for (size_t i = 0; i < saved_hashes.size(); ++i) { + EXPECT_TRUE(HashValuesEqual(saved_hashes[i])( + new_static_domain_state.pkp.spki_hashes[i])); + } - hash = std::find_if( - domain_state.dynamic_spki_hashes.begin(), - domain_state.dynamic_spki_hashes.end(), - HashValuesEqual(backup_hash)); - EXPECT_NE(domain_state.dynamic_spki_hashes.end(), hash); + // Expect the dynamic state to have pins. + TransportSecurityState::DomainState new_dynamic_domain_state; + EXPECT_TRUE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state)); + EXPECT_EQ(2UL, new_dynamic_domain_state.pkp.spki_hashes.size()); + EXPECT_TRUE(new_dynamic_domain_state.HasPublicKeyPins()); + + // Now set another header with max-age=0, and check that the pins are + // cleared in the dynamic state only. + header = "max-age = 0; " + good_pin + "; " + backup_pin; + EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); + + // Expect the static state to remain unchanged. + TransportSecurityState::DomainState new_static_domain_state2; + EXPECT_TRUE(state.GetStaticDomainState( + domain, sni_enabled, &new_static_domain_state2)); + EXPECT_EQ(saved_hashes.size(), + new_static_domain_state2.pkp.spki_hashes.size()); + for (size_t i = 0; i < saved_hashes.size(); ++i) { + EXPECT_TRUE(HashValuesEqual(saved_hashes[i])( + new_static_domain_state2.pkp.spki_hashes[i])); + } + + // Expect the dynamic pins to be gone. + TransportSecurityState::DomainState new_dynamic_domain_state2; + EXPECT_FALSE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state2)); + + // Expect the exact-matching static policy to continue to apply, even + // though dynamic policy has been removed. (This policy may change in the + // future, in which case this test must be updated.) + EXPECT_TRUE(state.HasPublicKeyPins(domain, true)); + EXPECT_TRUE(state.ShouldSSLErrorsBeFatal(domain, true)); + std::string failure_log; + // Damage the hashes to cause a pin validation failure. + new_static_domain_state2.pkp.spki_hashes[0].data()[0] ^= 0x80; + new_static_domain_state2.pkp.spki_hashes[1].data()[0] ^= 0x80; + EXPECT_FALSE(state.CheckPublicKeyPins( + domain, true, new_static_domain_state2.pkp.spki_hashes, &failure_log)); + EXPECT_NE(0UL, failure_log.length()); +} +#undef MAYBE_UpdateDynamicPKPMaxAge0 + +// Tests that when a static HSTS and a static HPKP entry are present, adding a +// dynamic HSTS header does not clobber the static HPKP entry. Further, adding a +// dynamic HPKP entry could not affect the HSTS entry for the site. +TEST_F(HttpSecurityHeadersTest, NoClobberPins) { + TransportSecurityState state; + TransportSecurityState::DomainState domain_state; + + // accounts.google.com has preloaded pins. + std::string domain = "accounts.google.com"; + + // Retrieve the DomainState as it is by default, including its known good + // pins. + const bool sni_enabled = true; + EXPECT_TRUE(state.GetStaticDomainState(domain, sni_enabled, &domain_state)); + HashValueVector saved_hashes = domain_state.pkp.spki_hashes; + EXPECT_TRUE(domain_state.ShouldUpgradeToSSL()); + EXPECT_TRUE(domain_state.HasPublicKeyPins()); + EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); + EXPECT_TRUE(state.HasPublicKeyPins(domain, sni_enabled)); + + // Add a dynamic HSTS header. CheckPublicKeyPins should still pass when given + // the original |saved_hashes|, indicating that the static PKP data is still + // configured for the domain. + EXPECT_TRUE(state.AddHSTSHeader(domain, "includesubdomains; max-age=10000")); + EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); + std::string failure_log; + EXPECT_TRUE(state.CheckPublicKeyPins( + domain, sni_enabled, saved_hashes, &failure_log)); + + // Add an HPKP header, which should only update the dynamic state. + HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1); + std::string good_pin = GetTestPin(1, HASH_VALUE_SHA1); + std::string backup_pin = GetTestPin(2, HASH_VALUE_SHA1); + std::string header = "max-age = 10000; " + good_pin + "; " + backup_pin; + + // Construct a fake SSLInfo that will pass AddHPKPHeader's checks. + SSLInfo ssl_info; + ssl_info.public_key_hashes.push_back(good_hash); + ssl_info.public_key_hashes.push_back(saved_hashes[0]); + EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); + + EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); + // HSTS should still be configured for this domain. + EXPECT_TRUE(domain_state.ShouldUpgradeToSSL()); + EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); + // The dynamic pins, which do not match |saved_hashes|, should take + // precedence over the static pins and cause the check to fail. + EXPECT_FALSE(state.CheckPublicKeyPins( + domain, sni_enabled, saved_hashes, &failure_log)); } }; // namespace net |