Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(710)

Side by Side Diff: content/browser/frame_host/mixed_content_navigation_throttle.cc

Issue 1905033002: PlzNavigate: Move navigation-level mixed content checks to the browser. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@console-security-message
Patch Set: Addressed nasko@'s comments. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/frame_host/mixed_content_navigation_throttle.h"
6
7 #include "base/memory/ptr_util.h"
8 #include "base/stl_util.h"
9 #include "content/browser/frame_host/frame_tree.h"
10 #include "content/browser/frame_host/frame_tree_node.h"
11 #include "content/browser/frame_host/navigation_handle_impl.h"
12 #include "content/browser/frame_host/render_frame_host_delegate.h"
13 #include "content/browser/renderer_host/render_view_host_impl.h"
14 #include "content/common/frame_messages.h"
15 #include "content/public/browser/content_browser_client.h"
16 #include "content/public/browser/render_frame_host.h"
17 #include "content/public/common/browser_side_navigation_policy.h"
18 #include "content/public/common/content_client.h"
19 #include "content/public/common/origin_util.h"
20 #include "content/public/common/web_preferences.h"
21 #include "net/base/url_util.h"
22 #include "url/gurl.h"
23 #include "url/origin.h"
24 #include "url/url_constants.h"
25 #include "url/url_util.h"
26
27 namespace {
28
29 using namespace content;
30
31 // Should return the same value as SchemeRegistry::shouldTreatURLSchemeAsSecure.
32 bool IsSecureScheme(const std::string& scheme) {
33 return base::ContainsValue(url::GetSecureSchemes(), scheme);
34 }
35
36 // Should return the same value as SchemeRegistry::shouldTreatURLSchemeAsSecure.
37 bool HasPotentiallySecureScheme(const GURL& url) {
nasko 2017/02/10 01:08:20 Do we still need this one if it is only used in on
carlosk 2017/02/11 01:40:22 See below.
carlosk 2017/02/14 01:42:59 For this one it indeed makes sense to eliminate it
38 return IsSecureScheme(url.scheme());
39 }
40
41 // Should return the same value as SecurityOrigin::isLocal and
42 // SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled.
43 bool ShouldTreatURLSchemeAsCORSEnabled(const GURL& url) {
nasko 2017/02/10 01:08:20 Same for this one, it is only used in one place.
carlosk 2017/02/11 01:40:22 I preferred keeping these here to make it clear th
44 return base::ContainsValue(url::GetCORSEnabledSchemes(), url.scheme());
45 }
46
47 // Should return the same value as SecurityOrigin::isSecure.
48 // TODO(carlosk): secure origin checks don't match between content and Blink
49 // hence this implementation here instead of a direct call to IsOriginSecure
50 // (in origin_util.cc). See https://crbug.com/629059.
51 bool IsOriginSecure(const GURL& url) {
52 if (HasPotentiallySecureScheme(url))
53 return true;
54
55 if (url.SchemeIsFileSystem() || url.SchemeIsBlob()) {
56 // Should use inner URL.
57 url::Origin origin(url);
58 if (IsSecureScheme(origin.scheme()))
59 return true;
60 }
61
62 return IsOriginWhiteListedTrustworthy(url::Origin(url));
63 }
64
65 // Should return the same value as the resource URL checks assigned to
66 // |isAllowed| made inside MixedContentChecker::isMixedContent.
67 bool IsUrlPotentiallySecure(const GURL& url) {
68 // blob: and filesystem: URLs never hit the network, and access is restricted
69 // to same-origin contexts, so they are not blocked.
70 bool is_secure =
71 url.SchemeIs(url::kBlobScheme) || url.SchemeIs(url::kFileSystemScheme) ||
72 IsOriginSecure(url) || IsPotentiallyTrustworthyOrigin(url::Origin(url));
73
74 // TODO(mkwst): Remove this once 'localhost' is no longer considered
75 // potentially trustworthy.
76 if (is_secure && url.SchemeIs(url::kHttpScheme) &&
77 net::IsLocalHostname(url.HostNoBrackets(), nullptr)) {
78 is_secure = false;
79 }
80
81 return is_secure;
82 }
83
84 // This method should return the same results as
85 // SchemeRegistry::shouldTreatURLSchemeAsRestrictingMixedContent.
86 bool DoesOriginSchemeRestricsMixedContent(const url::Origin& origin) {
87 return origin.scheme() == url::kHttpsScheme;
88 }
89
90 void UpdateRendererOnMixedContentFound(NavigationHandleImpl* handle_impl,
nasko 2017/02/10 01:08:20 nit: navigation_handle
carlosk 2017/02/11 01:40:22 Done.
91 const GURL& mixed_content_url,
92 bool was_allowed,
93 bool for_redirect) {
94 // TODO(carlosk): the root node should never be considered as being/having
95 // mixed content for now. Once/if the browser should also check form submits
96 // for mixed content than this will be allowed to happen and this DCHECK
97 // should be updated.
98 DCHECK(handle_impl->frame_tree_node()->parent());
99 RenderFrameHost* rfh = handle_impl->frame_tree_node()->current_frame_host();
100 rfh->Send(new FrameMsg_MixedContentFound(
101 rfh->GetRoutingID(), mixed_content_url, handle_impl->GetURL(),
102 handle_impl->request_context_type(), was_allowed, for_redirect));
103 }
104
105 } // namespace
106
107 namespace content {
108
109 // static
110 std::unique_ptr<NavigationThrottle>
111 MixedContentNavigationThrottle::CreateThrottleForNavigation(
112 NavigationHandle* navigation_handle) {
113 if (IsBrowserSideNavigationEnabled())
114 return base::WrapUnique(
115 new MixedContentNavigationThrottle(navigation_handle));
116 return nullptr;
117 }
118
119 MixedContentNavigationThrottle::MixedContentNavigationThrottle(
120 NavigationHandle* navigation_handle)
121 : NavigationThrottle(navigation_handle) {
122 DCHECK(IsBrowserSideNavigationEnabled());
123 }
124
125 MixedContentNavigationThrottle::~MixedContentNavigationThrottle() {}
126
127 ThrottleCheckResult MixedContentNavigationThrottle::WillStartRequest() {
128 bool should_block = ShouldBlockNavigation(false);
129 return should_block ? ThrottleCheckResult::CANCEL
nasko 2017/02/10 01:08:20 Should this be a cancel or a block?
carlosk 2017/02/11 01:40:22 We tested this together and figured it should inde
carlosk 2017/02/14 01:42:59 mkwst@: could you PTAL at this? More details: AFA
130 : ThrottleCheckResult::PROCEED;
131 }
132
133 ThrottleCheckResult MixedContentNavigationThrottle::WillRedirectRequest() {
134 // Upon redirects the same checks are to be executed as for requests.
135 bool should_block = ShouldBlockNavigation(true);
136 return should_block ? ThrottleCheckResult::CANCEL
137 : ThrottleCheckResult::PROCEED;
138 }
139
140 ThrottleCheckResult MixedContentNavigationThrottle::WillProcessResponse() {
141 // TODO(carlosk): At this point we are about to process the request response.
142 // So if we ever need to, here/now it is a good moment to check for the final
143 // attained security level of the connection. For instance, does it use an
144 // outdated protocol? The implementation should be based off
145 // MixedContentChecker::handleCertificateError. See https://crbug.com/576270.
146 return ThrottleCheckResult::PROCEED;
147 }
148
149 // Based off of MixedContentChecker::shouldBlockFetch.
150 bool MixedContentNavigationThrottle::ShouldBlockNavigation(bool for_redirect) {
151 NavigationHandleImpl* handle_impl =
152 static_cast<NavigationHandleImpl*>(navigation_handle());
153 FrameTreeNode* node = handle_impl->frame_tree_node();
154
155 // Find the parent node where mixed content is characterized, if any.
156 FrameTreeNode* mixed_content_node =
157 InWhichFrameIsContentMixed(node, handle_impl->GetURL());
158 if (!mixed_content_node) {
159 MaybeSendBlinkFeatureUsageReport();
160 return false;
161 }
162
163 // From this point on we know this is not a main frame navigation and that
164 // there is mixed content. Now let's decide if it's OK to proceed with it.
165
nasko 2017/02/10 01:08:21 nit: No need for an empty line.
carlosk 2017/02/11 01:40:22 Done.
166 const WebPreferences& prefs = mixed_content_node->current_frame_host()
167 ->render_view_host()
168 ->GetWebkitPreferences();
169
170 ReportBasicMixedContentFeatures(handle_impl->request_context_type(),
171 handle_impl->mixed_content_context_type(),
172 prefs);
173
174 // If we're in strict mode, we'll automagically fail everything, and
175 // intentionally skip the client/embedder checks in order to prevent degrading
176 // the site's security UI.
177 bool block_all_mixed_content = !!(
178 mixed_content_node->current_replication_state().insecure_request_policy &
179 blink::kBlockAllMixedContent);
180 bool strict_mode =
181 prefs.strict_mixed_content_checking || block_all_mixed_content;
182
183 blink::WebMixedContentContextType mixed_context_type =
184 handle_impl->mixed_content_context_type();
185
186 if (!ShouldTreatURLSchemeAsCORSEnabled(handle_impl->GetURL()))
187 mixed_context_type = blink::WebMixedContentContextType::OptionallyBlockable;
188
189 bool allowed = false;
190 RenderFrameHostDelegate* frame_host_delegate =
191 node->current_frame_host()->delegate();
192 switch (mixed_context_type) {
193 case blink::WebMixedContentContextType::OptionallyBlockable:
194 allowed = !strict_mode;
195 if (allowed) {
196 frame_host_delegate->PassiveInsecureContentFound(handle_impl->GetURL());
197 frame_host_delegate->DidDisplayInsecureContent();
198 }
199 break;
200
201 case blink::WebMixedContentContextType::Blockable: {
202 // Note: from the renderer side implementation it seems like we don't need
203 // to care about reporting
204 // blink::UseCounter::BlockableMixedContentInSubframeBlocked because it is
205 // only triggered for sub-resources which are not checked for in the
206 // browser.
207 bool should_ask_embedder =
208 !strict_mode && (!prefs.strictly_block_blockable_mixed_content ||
209 prefs.allow_running_insecure_content);
210 allowed =
211 should_ask_embedder &&
212 frame_host_delegate->ShouldAllowRunningInsecureContent(
213 handle_impl->GetWebContents(),
214 prefs.allow_running_insecure_content,
215 mixed_content_node->current_origin(), handle_impl->GetURL());
216 if (allowed) {
217 const GURL& origin_url = mixed_content_node->current_origin().GetURL();
carlosk 2017/02/08 02:59:03 nasko@: this was the only change related the conce
nasko 2017/02/10 01:08:20 Acknowledged.
218 frame_host_delegate->DidRunInsecureContent(origin_url,
219 handle_impl->GetURL());
220 GetContentClient()->browser()->RecordURLMetric(
221 "ContentSettings.MixedScript.RanMixedScript", origin_url);
222 mixed_content_features_.insert(MIXED_CONTENT_BLOCKABLE_ALLOWED);
223 }
224 break;
225 }
226
227 case blink::WebMixedContentContextType::ShouldBeBlockable:
228 allowed = !strict_mode;
229 if (allowed)
230 frame_host_delegate->DidDisplayInsecureContent();
231 break;
232
233 case blink::WebMixedContentContextType::NotMixedContent:
234 NOTREACHED();
235 break;
236 };
237
238 UpdateRendererOnMixedContentFound(
239 handle_impl, mixed_content_node->current_url(), allowed, for_redirect);
240 MaybeSendBlinkFeatureUsageReport();
241
242 return !allowed;
243 }
244
245 // This method mirrors MixedContentChecker::inWhichFrameIsContentMixed but is
246 // implemented in a different form that seems more appropriate here.
247 FrameTreeNode* MixedContentNavigationThrottle::InWhichFrameIsContentMixed(
248 FrameTreeNode* node,
249 const GURL& url) {
250 // Main frame navigations cannot be mixed content.
251 // TODO(carlosk): except for form submissions which might be supported in the
252 // future.
253 if (node->IsMainFrame())
254 return nullptr;
255
256 // There's no mixed content if any of these are true:
257 // - The navigated URL is potentially secure.
258 // - Neither the root nor parent frames have secure origins.
259 // This next section diverges in how the Blink version is implemented but
260 // should get to the same results. Especially where isMixedContent calls
261 // exist, here they are partially fulfilled here and partially replaced by
262 // DoesOriginSchemeRestricsMixedContent.
263 FrameTreeNode* mixed_content_node = nullptr;
264 FrameTreeNode* root = node->frame_tree()->root();
265 FrameTreeNode* parent = node->parent();
266 if (!IsUrlPotentiallySecure(url)) {
267 // TODO(carlosk): we might need to check more than just the immediate parent
268 // and the root. See https://crbug.com/623486.
269
270 // Checks if the root and then the immediate parent frames' origins are
271 // secure.
272 if (DoesOriginSchemeRestricsMixedContent(root->current_origin()))
273 mixed_content_node = root;
274 else if (DoesOriginSchemeRestricsMixedContent(parent->current_origin()))
275 mixed_content_node = parent;
276 }
277
278 // Note: The code below should behave the same way as the two calls to
279 // measureStricterVersionOfIsMixedContent from inside
280 // MixedContentChecker::inWhichFrameIs.
281 if (mixed_content_node) {
282 // We're currently only checking for mixed content in `https://*` contexts.
283 // What about other "secure" contexts the SchemeRegistry knows about? We'll
284 // use this method to measure the occurrence of non-webby mixed content to
285 // make sure we're not breaking the world without realizing it.
286 if (mixed_content_node->current_origin().scheme() != url::kHttpsScheme) {
287 mixed_content_features_.insert(
288 MIXED_CONTENT_IN_NON_HTTPS_FRAME_THAT_RESTRICTS_MIXED_CONTENT);
289 }
290 } else if (!IsOriginSecure(url) &&
291 (IsSecureScheme(root->current_origin().scheme()) ||
292 IsSecureScheme(parent->current_origin().scheme()))) {
293 mixed_content_features_.insert(
294 MIXED_CONTENT_IN_SECURE_FRAME_THAT_DOES_NOT_RESTRICT_MIXED_CONTENT);
295 }
296 return mixed_content_node;
297 }
298
299 void MixedContentNavigationThrottle::MaybeSendBlinkFeatureUsageReport() {
300 if (!mixed_content_features_.empty()) {
301 NavigationHandleImpl* handle_impl =
302 static_cast<NavigationHandleImpl*>(navigation_handle());
303 RenderFrameHost* rfh = handle_impl->frame_tree_node()->current_frame_host();
304 rfh->Send(new FrameMsg_BlinkFeatureUsageReport(rfh->GetRoutingID(),
305 mixed_content_features_));
306 mixed_content_features_.clear();
307 }
308 }
309
310 // Based off of MixedContentChecker::count.
311 void MixedContentNavigationThrottle::ReportBasicMixedContentFeatures(
312 RequestContextType request_context_type,
313 blink::WebMixedContentContextType mixed_content_context_type,
314 const WebPreferences& prefs) {
315 mixed_content_features_.insert(MIXED_CONTENT_PRESENT);
316
317 // Report any blockable content.
318 if (mixed_content_context_type ==
319 blink::WebMixedContentContextType::Blockable) {
320 mixed_content_features_.insert(MIXED_CONTENT_BLOCKABLE);
321 return;
322 }
323
324 // Note: as there's no mixed content checks for sub-resources on the browser
325 // side there should only be a subset of RequestContextType values that could
326 // ever be found here.
327 UseCounterFeature feature;
328 switch (request_context_type) {
329 case REQUEST_CONTEXT_TYPE_INTERNAL:
330 feature = MIXED_CONTENT_INTERNAL;
331 break;
332 case REQUEST_CONTEXT_TYPE_PREFETCH:
333 feature = MIXED_CONTENT_PREFETCH;
334 break;
335
336 case REQUEST_CONTEXT_TYPE_AUDIO:
337 case REQUEST_CONTEXT_TYPE_DOWNLOAD:
338 case REQUEST_CONTEXT_TYPE_FAVICON:
339 case REQUEST_CONTEXT_TYPE_IMAGE:
340 case REQUEST_CONTEXT_TYPE_PLUGIN:
341 case REQUEST_CONTEXT_TYPE_VIDEO:
342 default:
343 NOTREACHED() << "RequestContextType has value " << request_context_type
344 << " and has WebMixedContentContextType of "
345 << static_cast<int>(mixed_content_context_type);
346 return;
347 }
348 mixed_content_features_.insert(feature);
349 }
350
351 // static
352 bool MixedContentNavigationThrottle::IsMixedContentForTesting(
353 const GURL& origin_url,
354 const GURL& url) {
355 const url::Origin origin(origin_url);
356 return !IsUrlPotentiallySecure(url) &&
357 DoesOriginSchemeRestricsMixedContent(origin);
358 }
359
360 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698