File: insecure_form_navigation_throttle.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (174 lines) | stat: -rw-r--r-- 6,930 bytes parent folder | download | duplicates (5)
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
// 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 {

void LogMixedFormInterstitialMetrics(
    security_interstitials::InsecureFormNavigationThrottle::
        InterstitialTriggeredState state) {
  base::UmaHistogramEnumeration("Security.MixedForm.InterstitialTriggerState",
                                state);
}

}  // namespace

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;
  }

  InterstitialTriggeredState log_state =
      InterstitialTriggeredState::kMixedFormDirect;
  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()) {
      log_state = InterstitialTriggeredState::kMixedFormRedirectWithFormData;
    } else {
      log_state = InterstitialTriggeredState::kMixedFormRedirectNoFormData;
      should_proceed = true;
    }
  }

  if (should_proceed) {
    LogMixedFormInterstitialMetrics(log_state);
    return content::NavigationThrottle::PROCEED;
  }

  LogMixedFormInterstitialMetrics(log_state);

  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