blob: 632204a7e64705e47e08396007376123402e432d [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// 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 <utility>
#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/insecure_form_util.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_response_headers.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 security_interstitials {
InsecureFormNavigationThrottle::InsecureFormNavigationThrottle(
content::NavigationThrottleRegistry& registry,
std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory)
: content::NavigationThrottle(registry),
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, except when prerendering.
InsecureFormTabStorage* tab_storage = InsecureFormTabStorage::FromWebContents(
navigation_handle()->GetWebContents());
if (tab_storage && !navigation_handle()->IsInPrerenderedMainFrame()) {
tab_storage->SetIsProceeding(false);
tab_storage->SetInterstitialShown(false);
}
return content::NavigationThrottle::PROCEED;
}
const char* InsecureFormNavigationThrottle::GetNameForLogging() {
return "InsecureFormNavigationThrottle";
}
// static
void InsecureFormNavigationThrottle::MaybeCreateAndAdd(
content::NavigationThrottleRegistry& registry,
std::unique_ptr<SecurityBlockingPageFactory> blocking_page_factory,
PrefService* prefs) {
if (prefs && !prefs->GetBoolean(prefs::kMixedFormsWarningsEnabled)) {
return;
}
registry.AddThrottle(std::make_unique<InsecureFormNavigationThrottle>(
registry, std::move(blocking_page_factory)));
}
content::NavigationThrottle::ThrottleCheckResult
InsecureFormNavigationThrottle::GetThrottleResultForMixedForm(
bool is_redirect) {
content::NavigationHandle* handle = navigation_handle();
content::WebContents* contents = handle->GetWebContents();
InsecureFormTabStorage* tab_storage =
InsecureFormTabStorage::FromWebContents(contents);
// We only show insecure form interstitials for form submissions. However GET
// submissions are not marked as form submissions on reloads, so we check if
// this navigation is coming from another mixed form interstitial.
if (!handle->IsFormSubmission() &&
(handle->IsInPrerenderedMainFrame() ||
(!tab_storage || !tab_storage->InterstitialShown()))) {
return content::NavigationThrottle::PROCEED;
}
// If the form is in a prerendered page, cancel it. Even though the form
// submission wouldn't include user data (a prerender cannot provide any
// input), the prerendered form submission could still leak data over the
// network (e.g. the path).
// There's an exception to this: Reloading a GET form will proceed since a
// prerender shouldn't check the InsecureFormTabStorage, which is a per-tab
// object. This is done in the check above.
if (handle->IsInPrerenderedMainFrame()) {
return content::NavigationThrottle::CANCEL;
}
// If user has just chosen to proceed on an interstitial, we don't show
// another one.
if (tab_storage && tab_storage->IsProceeding()) {
return content::NavigationThrottle::PROCEED;
}
// Do not set special error page HTML for insecure forms in subframes; those
// are already hard blocked.
if (!handle->IsInOutermostMainFrame()) {
return content::NavigationThrottle::PROCEED;
}
url::Origin form_originating_origin =
handle->GetInitiatorOrigin().value_or(url::Origin());
if (!security_interstitials::IsInsecureFormActionOnSecureSource(
form_originating_origin, handle->GetURL())) {
// Currently we only warn for insecure forms in secure pages.
return content::NavigationThrottle::PROCEED;
}
bool should_proceed = false;
if (is_redirect) {
// 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()) {
} else {
should_proceed = true;
}
}
if (should_proceed) {
return content::NavigationThrottle::PROCEED;
}
std::unique_ptr<InsecureFormBlockingPage> blocking_page =
blocking_page_factory_->CreateInsecureFormBlockingPage(contents,
handle->GetURL());
std::string interstitial_html = blocking_page->GetHTMLContents();
SecurityInterstitialTabHelper::AssociateBlockingPage(
handle, std::move(blocking_page));
if (!tab_storage) {
tab_storage = InsecureFormTabStorage::GetOrCreate(contents);
}
tab_storage->SetInterstitialShown(true);
return content::NavigationThrottle::ThrottleCheckResult(
CANCEL, net::ERR_BLOCKED_BY_CLIENT, std::move(interstitial_html));
}
} // namespace security_interstitials