1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ssl/https_upgrades_util.h"
#include "base/feature_list.h"
#include "base/values.h"
#include "chrome/browser/ssl/chrome_security_blocking_page_factory.h"
#include "chrome/browser/ssl/https_upgrades_interceptor.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/prefs/pref_service.h"
#include "components/security_interstitials/core/https_only_mode_metrics.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
using HttpInterstitialState =
security_interstitials::https_only_mode::HttpInterstitialState;
bool IsHostnameInHttpAllowlist(const GURL& url, PrefService* prefs) {
const base::Value::List& allowed_hosts =
prefs->GetList(prefs::kHttpAllowlist);
// Though this is not technically a Content Setting, ContentSettingsPattern
// aligns better than URLMatcher with the rules from
// https://chromeenterprise.google/policies/url-patterns/.
for (const auto& value : allowed_hosts) {
if (!value.is_string()) {
continue;
}
auto pattern = ContentSettingsPattern::FromString(value.GetString());
// Blanket host wildcard patterns are not allowed (matching every host),
// because admins should instead explicitly disable upgrades using the
// HttpsOnlyMode policy.
if (pattern.IsValid() && !pattern.MatchesAllHosts() &&
pattern.Matches(url)) {
return true;
}
}
return false;
}
void AllowHttpForHostnamesForTesting(const std::vector<std::string>& hostnames,
PrefService* prefs) {
DCHECK(prefs->GetList(prefs::kHttpAllowlist).empty());
base::Value::List allowed_hosts;
for (const std::string& hostname : hostnames) {
allowed_hosts.Append(hostname);
}
prefs->SetList(prefs::kHttpAllowlist, std::move(allowed_hosts));
}
void ClearHttpAllowlistForHostnamesForTesting(PrefService* prefs) {
base::Value::List empty_list;
prefs->SetList(prefs::kHttpAllowlist, std::move(empty_list));
}
bool IsBalancedModeAvailable() {
return base::FeatureList::IsEnabled(features::kHttpsFirstBalancedMode);
}
bool IsBalancedModeEnabled(PrefService* prefs) {
if (!prefs || !IsBalancedModeAvailable()) {
return false;
}
bool user_has_modified_settings =
prefs->HasPrefPath(prefs::kHttpsOnlyModeEnabled) ||
prefs->HasPrefPath(prefs::kHttpsFirstBalancedMode);
if (!user_has_modified_settings) {
return base::FeatureList::IsEnabled(
features::kHttpsFirstBalancedModeAutoEnable);
}
return prefs->GetBoolean(prefs::kHttpsFirstBalancedMode);
}
bool IsBalancedModeInterstitialEnabledByHeuristics(
const HttpInterstitialState& state) {
return IsBalancedModeAvailable() &&
(state.enabled_by_engagement_heuristic ||
state.enabled_by_typically_secure_browsing);
}
bool IsBalancedModeUniquelyEnabled(const HttpInterstitialState& state) {
// Balance mode is _uniquely_ enabled only when other HFM variants aren't
// enabled.
if (state.enabled_by_pref) {
return false;
}
if (base::FeatureList::IsEnabled(features::kHttpsFirstModeIncognito) &&
state.enabled_by_incognito) {
return false;
}
// ...then ensure balanced mode is enabled.
return (IsBalancedModeAvailable() && state.enabled_in_balanced_mode) ||
IsBalancedModeInterstitialEnabledByHeuristics(state);
}
bool IsInterstitialEnabled(const HttpInterstitialState& state) {
// Interstitials are enabled when "strict" interstitials are enabled...
if (IsStrictInterstitialEnabled(state)) {
return true;
}
if (IsBalancedModeAvailable() && state.enabled_in_balanced_mode) {
return true;
}
return IsBalancedModeInterstitialEnabledByHeuristics(state);
}
bool IsStrictInterstitialEnabled(const HttpInterstitialState& state) {
if (state.enabled_by_pref) {
return true;
}
if (base::FeatureList::IsEnabled(features::kHttpsFirstModeIncognito) &&
state.enabled_by_incognito) {
return true;
}
return false;
}
bool ShouldExemptNonUniqueHostnames(const HttpInterstitialState& state) {
// If strict mode is enabled by the pref, warn the user before any HTTP that
// goes over the network. Any other mode ignores non-unique hostnames.
return !state.enabled_by_pref;
}
bool ShouldExcludeUrlFromInterstitial(const HttpInterstitialState& state,
const GURL& url) {
// In balanced mode, single-label hostnames and URLs with non-default ports
// are excluded from interstitials. This also applies if one of the HFM
// heuristics enabled Balanced Mode.
return IsBalancedModeUniquelyEnabled(state) &&
(net::GetSuperdomain(url.host()).empty() ||
(url.has_port() &&
url.IntPort() != HttpsUpgradesInterceptor::GetHttpPortForTesting()));
}
bool MustDisableSiteEngagementHeuristic(Profile* profile) {
return !base::FeatureList::IsEnabled(
features::kHttpsFirstModeV2ForEngagedSites) ||
!IsBalancedModeAvailable() ||
ChromeSecurityBlockingPageFactory::IsEnterpriseManaged(profile);
}
bool MustDisableTypicallySecureUserHeuristic(Profile* profile) {
return !base::FeatureList::IsEnabled(
features::kHttpsFirstModeV2ForTypicallySecureUsers) ||
!IsBalancedModeAvailable() ||
ChromeSecurityBlockingPageFactory::IsEnterpriseManaged(profile);
}
void RecordHttpsFirstModeUKM(
ukm::SourceId source_id,
security_interstitials::https_only_mode::BlockingResult result) {
if (source_id == ukm::kInvalidSourceId) {
return;
}
ukm::UkmRecorder* ukm_recorder = ukm::UkmRecorder::Get();
CHECK(ukm_recorder);
ukm::builders::HttpsFirstMode_Event(source_id)
.SetResult(static_cast<int>(result))
.Record(ukm_recorder);
}
ScopedAllowHttpForHostnamesForTesting::ScopedAllowHttpForHostnamesForTesting(
const std::vector<std::string>& hostnames,
PrefService* prefs)
: prefs_(prefs) {
AllowHttpForHostnamesForTesting(hostnames, prefs);
}
ScopedAllowHttpForHostnamesForTesting::
~ScopedAllowHttpForHostnamesForTesting() {
ClearHttpAllowlistForHostnamesForTesting(prefs_);
}
|