blob: fd2b625f12f554b801190fbbb8a5dec5ad84e52e [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/security_interstitials/content/insecure_form_navigation_throttle.h"
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "components/prefs/pref_service.h"
#include "components/security_interstitials/content/insecure_form_blocking_page.h"
#include "components/security_interstitials/content/insecure_form_tab_storage.h"
#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
#include "components/security_interstitials/core/features.h"
#include "components/security_interstitials/core/pref_names.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "url/origin.h"
#include "url/url_constants.h"
namespace {
void LogMixedFormInterstitialMetrics(
security_interstitials::InsecureFormNavigationThrottle::
InterstitialTriggeredState state) {
base::UmaHistogramEnumeration("Security.MixedForm.InterstitialTriggerState",
state);
}
bool IsInsecureFormAction(const GURL& action_url) {
if (action_url.SchemeIs(url::kBlobScheme) ||
action_url.SchemeIs(url::kFileSystemScheme))
return false;
return !network::IsOriginPotentiallyTrustworthy(
url::Origin::Create(action_url));
}
} // namespace
namespace security_interstitials {
InsecureFormNavigationThrottle::InsecureFormNavigationThrottle(
content::NavigationHandle* navigation_handle,
std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory)
: content::NavigationThrottle(navigation_handle),
blocking_page_factory_(std::move(blocking_page_factory)) {}
InsecureFormNavigationThrottle::~InsecureFormNavigationThrottle() = default;
content::NavigationThrottle::ThrottleCheckResult
InsecureFormNavigationThrottle::WillStartRequest() {
return GetThrottleResultForMixedForm(false /* is_redirect */);
}
content::NavigationThrottle::ThrottleCheckResult
InsecureFormNavigationThrottle::WillRedirectRequest() {
return GetThrottleResultForMixedForm(true /* is_redirect */);
}
content::NavigationThrottle::ThrottleCheckResult
InsecureFormNavigationThrottle::WillProcessResponse() {
// If there is an InsecureFormTabStorage associated to |web_contents_|, clear
// the IsProceeding flag.
InsecureFormTabStorage* tab_storage = InsecureFormTabStorage::FromWebContents(
navigation_handle()->GetWebContents());
if (tab_storage)
tab_storage->SetIsProceeding(false);
return content::NavigationThrottle::PROCEED;
}
const char* InsecureFormNavigationThrottle::GetNameForLogging() {
return "InsecureFormNavigationThrottle";
}
// static
std::unique_ptr<InsecureFormNavigationThrottle>
InsecureFormNavigationThrottle::MaybeCreateNavigationThrottle(
content::NavigationHandle* navigation_handle,
std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory,
PrefService* prefs) {
if (!base::FeatureList::IsEnabled(kInsecureFormSubmissionInterstitial) ||
(prefs && !prefs->GetBoolean(prefs::kMixedFormsWarningsEnabled)))
return nullptr;
return std::make_unique<InsecureFormNavigationThrottle>(
navigation_handle, std::move(blocking_page_factory));
}
content::NavigationThrottle::ThrottleCheckResult
InsecureFormNavigationThrottle::GetThrottleResultForMixedForm(
bool is_redirect) {
content::NavigationHandle* handle = navigation_handle();
if (!handle->IsFormSubmission())
return content::NavigationThrottle::PROCEED;
content::WebContents* contents = handle->GetWebContents();
// Do not set special error page HTML for insecure forms in subframes; those
// are already hard blocked.
if (!handle->IsInMainFrame()) {
return content::NavigationThrottle::PROCEED;
}
url::Origin form_originating_origin =
handle->GetInitiatorOrigin().value_or(url::Origin());
if (!IsInsecureFormAction(handle->GetURL()) ||
!(form_originating_origin.scheme() == url::kHttpsScheme)) {
// Currently we only warn for insecure forms in secure pages.
return content::NavigationThrottle::PROCEED;
}
// If user has just chosen to proceed on an interstitial, we don't show
// another one.
InsecureFormTabStorage* tab_storage =
InsecureFormTabStorage::GetOrCreate(contents);
if (tab_storage->IsProceeding())
return content::NavigationThrottle::PROCEED;
if (is_redirect) {
std::string feature_mode = base::GetFieldTrialParamValueByFeature(
kInsecureFormSubmissionInterstitial,
kInsecureFormSubmissionInterstitialMode);
// 307 and 308 redirects for POST forms are special because they can leak
// form data if done over HTTP.
if ((handle->GetResponseHeaders()->response_code() ==
net::HTTP_TEMPORARY_REDIRECT ||
handle->GetResponseHeaders()->response_code() ==
net::HTTP_PERMANENT_REDIRECT) &&
handle->IsPost()) {
LogMixedFormInterstitialMetrics(
InterstitialTriggeredState::kMixedFormRedirectWithFormData);
if (feature_mode !=
kInsecureFormSubmissionInterstitialModeIncludeRedirects &&
feature_mode !=
kInsecureFormSubmissionInterstitialModeIncludeRedirectsWithFormData) {
return content::NavigationThrottle::PROCEED;
}
} else {
LogMixedFormInterstitialMetrics(
InterstitialTriggeredState::kMixedFormRedirectNoFormData);
if (feature_mode !=
kInsecureFormSubmissionInterstitialModeIncludeRedirects) {
return content::NavigationThrottle::PROCEED;
}
}
} else {
LogMixedFormInterstitialMetrics(
InterstitialTriggeredState::kMixedFormDirect);
}
std::unique_ptr<InsecureFormBlockingPage> blocking_page =
blocking_page_factory_->CreateInsecureFormBlockingPage(contents,
handle->GetURL());
std::string interstitial_html = blocking_page->GetHTMLContents();
SecurityInterstitialTabHelper::AssociateBlockingPage(
contents, handle->GetNavigationId(), std::move(blocking_page));
return content::NavigationThrottle::ThrottleCheckResult(
CANCEL, net::ERR_BLOCKED_BY_CLIENT, interstitial_html);
}
} // namespace security_interstitials