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

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: Address jam@ comments; many minor code and comment updates. Created 3 years, 11 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 2016 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 "content/browser/frame_host/frame_tree.h"
8 #include "content/browser/frame_host/frame_tree_node.h"
9 #include "content/browser/frame_host/navigation_handle_impl.h"
10 #include "content/browser/frame_host/render_frame_host_delegate.h"
11 #include "content/browser/renderer_host/render_view_host_impl.h"
12 #include "content/common/frame_messages.h"
13 #include "content/public/browser/content_browser_client.h"
14 #include "content/public/browser/render_frame_host.h"
15 #include "content/public/common/browser_side_navigation_policy.h"
16 #include "content/public/common/content_client.h"
17 #include "content/public/common/origin_util.h"
18 #include "content/public/common/url_constants.h"
19 #include "content/public/common/web_preferences.h"
20 #include "net/base/url_util.h"
21 #include "url/gurl.h"
22 #include "url/origin.h"
23 #include "url/url_constants.h"
24
25 namespace {
26
27 using namespace content;
28
29 // Should return the same value as SchemeRegistry::shouldTreatURLSchemeAsSecure.
30 // TODO(carlosk): we have to figure out how to share schemes registered within
31 // Blink's SchemeRegistry with content/browser code. Should statically defined
32 // ones in Chrome/embedder come from a shared file? Should dynamically defined
33 // ones from extensions register both with browser and renderer code? See
34 // https://crbug.com/627502.
35 bool IsSecureScheme(const std::string& scheme) {
36 // Note: default schemes for URLSchemesRegistry::secureSchemes.
37 bool result = scheme == url::kHttpsScheme || scheme == url::kAboutScheme ||
38 scheme == url::kDataScheme || scheme == url::kWssScheme;
39
40 // Note: below are the schemes registered through registerURLSchemeAsSecure.
41 // Note: here and for other scheme "registration" code below some
42 // registrations should not happen as they depend on the target being built. I
43 // tried limiting that by using platform IF-DEF-s but it is insufficient.
44
45 // Registered from content/renderer/render_thread_impl.cc.
46 result |= scheme == kChromeUIScheme;
47 // Registered from chrome/common/url_constants.cc.
48 result |= scheme == "chrome-search";
49 #if !defined(OS_ANDROID) && !defined(OS_IOS)
50 // Registered from extensions/renderer/dispatcher.cc.
51 result |= scheme == "chrome-extension";
52 #endif
53 #if defined(OS_ANDROID)
54 // Registered from android_webview/renderer/aw_content_renderer_client.cc.
55 result |= scheme == "android-webview-video-poster";
56 #endif
57 return result;
58 }
59
60 // Should return the same value as SchemeRegistry::shouldTreatURLSchemeAsSecure.
61 bool HasPotentiallySecureScheme(const GURL& url) {
62 return IsSecureScheme(url.scheme());
63 }
64
65 // Should return the same value as SecurityOrigin::isLocal (and consequentially
66 // SchemeRegistry::shouldTreatURLSchemeAsLocal).
67 // TODO(carlosk): Same registration problem as described in IsSecureScheme
68 // applies here. See https://crbug.com/627502.
69 bool HasLocalScheme(const GURL& url) {
70 // Note: default schemes for URLSchemesRegistry::localSchemes.
71 bool result = url.SchemeIs(url::kFileScheme);
72
73 // Note: below are the schemes registered through registerURLSchemeAsLocal.
74
75 #if defined(OS_CHROMEOS)
76 // Registered from chrome/renderer/chrome_content_renderer_client.cc.
77 result |= url.SchemeIs(content::kExternalFileScheme);
78 #endif
79 #if defined(OS_ANDROID)
80 // Registered from chrome/renderer/chrome_content_renderer_client.cc and
81 // from android_webview/renderer/aw_content_renderer_client.cc.
82 result |= url.SchemeIs(url::kContentScheme);
83 #endif
84
85 return result;
86 }
87
88 // This reflects the result expected from SecurityOrigin::isUnique (not its
89 // logic). It takes into account how SecurityOrigin::create might return unique
90 // origins for URLS that cause SchemeRegistry::shouldTreatURLSchemeAsNoAccess to
91 // return true.
92 // TODO(carlosk): Same registration problem as described in IsSecureScheme
93 // applies here. See https://crbug.com/627502.
94 bool IsUniqueScheme(const GURL& url) {
95 // Note: default schemes for URLSchemesRegistry::schemesWithUniqueOrigins.
96 bool result = url.SchemeIs(url::kAboutScheme) ||
97 url.SchemeIs(url::kJavaScriptScheme) ||
98 url.SchemeIs(url::kDataScheme);
99
100 // Note: below are the schemes registered through registerURLSchemeAsNoAccess.
101
102 // Registered from chrome/renderer/chrome_render_thread_observer.cc.
103 result |= url.SchemeIs("chrome-native");
104
105 return result;
106 }
107
108 // Should return the same value as SecurityOrigin::isLocal and
109 // SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled.
110 // TODO(carlosk): Same registration problem as described in IsSecureScheme
111 // applies here. See https://crbug.com/627502.
112 bool ShouldTreatURLSchemeAsCORSEnabled(const GURL& url) {
113 // Note: default schemes for URLSchemesRegistry::CORSEnabledSchemes.
114 bool result = url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kDataScheme);
115
116 // Note: below are the schemes registered through
117 // registerURLSchemeAsCORSEnabled.
118
119 // Registered from content/renderer/render_thread_impl.cc.
120 result |= url.SchemeIs(kChromeUIScheme);
121 #if !defined(OS_ANDROID) && !defined(OS_IOS)
122 // Registered from extensions/renderer/dispatcher.cc.
123 result |= url.SchemeIs("chrome-extension");
124 #endif
125
126 return result;
127 }
128
129 // Should return the same value as SecurityOrigin::isSecure.
130 bool SecurityOriginIsSecure(const GURL& url) {
131 return HasPotentiallySecureScheme(url) ||
132 (url.SchemeIsFileSystem() && url.inner_url() &&
133 HasPotentiallySecureScheme(*url.inner_url())) ||
134 (url.SchemeIsBlob() &&
135 HasPotentiallySecureScheme(GURL(url.GetContent()))) ||
136 IsOriginWhiteListedTrustworthy(url);
137 }
138
139 // Should return the same value as the resource URL checks made inside
140 // MixedContentChecker::isMixedContent.
141 bool IsUrlPotentiallySecure(const GURL& url) {
142 // TODO(carlosk): secure origin checks don't match between content and Blink
143 // hence this implementation here instead of a direct call to IsOriginSecure
144 // (in origin_util.cc). See https://crbug.com/629059.
145
146 bool is_secure = SecurityOriginIsSecure(url);
147
148 // blob: and filesystem: URLs never hit the network, and access is restricted
149 // to same-origin contexts, so they are not blocked either.
150 if (url.SchemeIs(url::kBlobScheme) || url.SchemeIs(url::kFileSystemScheme))
151 is_secure |= true;
152
153 // These next checks mimics the behavior of
154 // SecurityOrigin::isPotentiallyTrustworthy without duplicating much of the
155 // checks already performed previously (hence this not being enclosed in
156 // another method). The logic here will consider a unique scheme to be secure
157 // as done when SecurityOrigin::m_isUniqueOriginPotentiallyTrustworthy is
158 // true.
159 if (IsUniqueScheme(url) || HasLocalScheme(url) ||
160 net::IsLocalhost(url.HostNoBrackets()))
161 is_secure |= true;
162
163 // TODO(mkwst): Remove this once 'localhost' is no longer considered
164 // potentially trustworthy.
165 if (is_secure && url.SchemeIs(url::kHttpScheme) &&
166 net::IsLocalHostname(url.HostNoBrackets(), nullptr)) {
167 is_secure = false;
168 }
169
170 return is_secure;
171 }
172
173 // This method should return the same results as
174 // SchemeRegistry::shouldTreatURLSchemeAsRestrictingMixedContent.
175 bool DoesOriginSchemeRestricsMixedContent(const url::Origin& origin) {
176 return origin.scheme() == url::kHttpsScheme;
177 }
178
179 void UpdateRendererOnMixedContentFound(NavigationHandleImpl* handle_impl,
180 const GURL& mixed_content_url,
181 bool was_allowed,
182 bool for_redirect) {
183 // TODO(carlosk): the root node should never be considered as being/having
184 // mixed content for now. Once/if the browser should also check form submits
185 // for mixed content than this will be allowed to happen and this DCHECK
186 // should be updated.
187 DCHECK(handle_impl->frame_tree_node()->parent());
188 RenderFrameHost* rfh = handle_impl->frame_tree_node()->current_frame_host();
189 rfh->Send(new FrameMsg_MixedContentFound(
190 rfh->GetRoutingID(), mixed_content_url, handle_impl->GetURL(),
191 handle_impl->request_context_type(), was_allowed, for_redirect));
192 }
193
194 } // namespace
195
196 namespace content {
197
198 MixedContentNavigationThrottle::MixedContentNavigationThrottle(
199 NavigationHandle* navigation_handle)
200 : NavigationThrottle(navigation_handle) {
201 DCHECK(IsBrowserSideNavigationEnabled());
202 }
203
204 MixedContentNavigationThrottle::~MixedContentNavigationThrottle() {}
205
206 ThrottleCheckResult MixedContentNavigationThrottle::WillStartRequest() {
207 bool should_block = ShouldBlockNavigation(false);
208 return should_block ? ThrottleCheckResult::CANCEL
209 : ThrottleCheckResult::PROCEED;
210 }
211
212 ThrottleCheckResult MixedContentNavigationThrottle::WillRedirectRequest() {
213 // Upon redirects the same checks are to be executed as for requests.
214 bool should_block = ShouldBlockNavigation(true);
215 return should_block ? ThrottleCheckResult::CANCEL
216 : ThrottleCheckResult::PROCEED;
217 }
218
219 ThrottleCheckResult MixedContentNavigationThrottle::WillProcessResponse() {
220 // TODO(carlosk): At this point we are about to process the request response.
221 // So here/now it is a good moment to check for the final attained security
222 // level of the connection. For instance: does it use an outdated protocol?
nasko 2017/01/12 18:32:37 nit: s/:/,/
carlosk 2017/01/21 02:54:58 Done.
223 // The implementation should be based off
224 // MixedContentChecker::handleCertificateError.
nasko 2017/01/12 18:32:37 Should this be fixed before we consider mixed cont
carlosk 2017/01/21 02:54:58 I am honestly unsure about this. This is checked m
carlosk 2017/02/08 02:59:02 Bumping this comment as I had not specific respons
225 return ThrottleCheckResult::PROCEED;
226 }
227
228 // Based off of MixedContentChecker::shouldBlockFetch.
229 bool MixedContentNavigationThrottle::ShouldBlockNavigation(bool for_redirect) {
230 NavigationHandleImpl* handle_impl =
231 static_cast<NavigationHandleImpl*>(navigation_handle());
232 FrameTreeNode* node = handle_impl->frame_tree_node();
233
234 // Find the parent node where mixed content is characterized, if any.
235 FrameTreeNode* mixed_content_node =
236 InWhichFrameIsContentMixed(node, handle_impl->GetURL());
237 if (!mixed_content_node) {
238 MaybeSendBlinkFeatureUsageReport();
239 return false;
240 }
241
242 // From this point on we know this is not a main frame navigation and that
243 // there is mixed content. Now let's decide if it's OK to proceed with it.
244
245 const WebPreferences& prefs = mixed_content_node->current_frame_host()
246 ->render_view_host()
247 ->GetWebkitPreferences();
248
249 ReportBasicMixedContentFeatures(handle_impl->request_context_type(),
250 handle_impl->mixed_content_context_type(),
251 prefs);
252
253 // If we're in strict mode, we'll automagically fail everything, and
254 // intentionally skip the client/embedder checks in order to prevent degrading
255 // the site's security UI.
256 bool block_all_mixed_content = !!(
257 mixed_content_node->current_replication_state().insecure_request_policy &
258 blink::kBlockAllMixedContent);
259 bool strict_mode =
260 prefs.strict_mixed_content_checking || block_all_mixed_content;
261
262 blink::WebMixedContentContextType mixed_context_type =
263 handle_impl->mixed_content_context_type();
264
265 if (!ShouldTreatURLSchemeAsCORSEnabled(handle_impl->GetURL())) {
266 mixed_context_type = blink::WebMixedContentContextType::OptionallyBlockable;
267 }
268
269 bool allowed = false;
270 RenderFrameHostDelegate* frame_host_delegate =
271 node->current_frame_host()->delegate();
272 switch (mixed_context_type) {
273 case blink::WebMixedContentContextType::OptionallyBlockable:
274 allowed = !strict_mode;
275 if (allowed) {
276 frame_host_delegate->PassiveInsecureContentFound(handle_impl->GetURL());
277 frame_host_delegate->DidDisplayInsecureContent();
278 }
279 break;
280
281 case blink::WebMixedContentContextType::Blockable: {
282 // Note: from the renderer side implementation it seems like we don't need
283 // to care about reporting
284 // blink::UseCounter::BlockableMixedContentInSubframeBlocked because it is
285 // only triggered for sub-resources which are not checked for in the
286 // browser.
287 bool shouldAskEmbedder =
nasko 2017/01/12 18:32:37 nit: should_ask_embedder, this is not Blink code a
carlosk 2017/01/21 02:54:58 Done.
288 !strict_mode && (!prefs.strictly_block_blockable_mixed_content ||
289 prefs.allow_running_insecure_content);
290 allowed = shouldAskEmbedder &&
291 frame_host_delegate->ShouldAllowRunningInsecureContent(
292 prefs.allow_running_insecure_content,
293 mixed_content_node->current_origin(), handle_impl->GetURL(),
294 handle_impl->GetWebContents());
295 if (allowed) {
296 const GURL& origin_url = mixed_content_node->current_url().GetOrigin();
297 frame_host_delegate->DidRunInsecureContent(origin_url,
298 handle_impl->GetURL());
299 GetContentClient()->browser()->RecordURLMetric(
300 "ContentSettings.MixedScript.RanMixedScript", origin_url);
301 mixed_content_features_.insert(MIXED_CONTENT_BLOCKABLE_ALLOWED);
302 }
303 break;
304 }
305
306 case blink::WebMixedContentContextType::ShouldBeBlockable:
307 allowed = !strict_mode;
308 if (allowed)
309 frame_host_delegate->DidDisplayInsecureContent();
310 break;
311
312 case blink::WebMixedContentContextType::NotMixedContent:
313 NOTREACHED();
314 break;
315 };
316
317 UpdateRendererOnMixedContentFound(
318 handle_impl, mixed_content_node->current_url(), allowed, for_redirect);
319 MaybeSendBlinkFeatureUsageReport();
320
321 return !allowed;
322 }
323
324 FrameTreeNode* MixedContentNavigationThrottle::InWhichFrameIsContentMixed(
325 FrameTreeNode* node,
326 const GURL& url) {
327 // Main frame navigations cannot be mixed content.
328 // TODO(carlosk): except for form submissions which might be supported in the
329 // future.
330 if (node->IsMainFrame())
331 return nullptr;
332
333 // There's no mixed content if any of these are true:
334 // - The navigated URL is potentially secure.
335 // - Neither the root nor parent frames have secure origins.
336 FrameTreeNode* mixed_content_node = nullptr;
337 FrameTreeNode* root = node->frame_tree()->root();
338 FrameTreeNode* parent = node->parent();
339 if (!IsUrlPotentiallySecure(url)) {
340 // TODO(carlosk): we might need to check more than just the immediate parent
341 // and the root. See https://crbug.com/623486.
342
343 // Checks if the root and then the immediate parent frames' origins are
344 // secure.
345 if (DoesOriginSchemeRestricsMixedContent(root->current_origin()))
346 mixed_content_node = root;
347 else if (DoesOriginSchemeRestricsMixedContent(parent->current_origin()))
348 mixed_content_node = parent;
349 }
350
351 // Note: This code below should behave the same way as as the two calls to
nasko 2017/01/12 18:32:37 nit: s/as as/as/
carlosk 2017/01/21 02:54:58 Done.
352 // measureStricterVersionOfIsMixedContent from inside
353 // MixedContentChecker::inWhichFrameIs.
354 if (mixed_content_node) {
355 // We're currently only checking for mixed content in `https://*` contexts.
356 // What about other "secure" contexts the SchemeRegistry knows about? We'll
357 // use this method to measure the occurrence of non-webby mixed content to
358 // make sure we're not breaking the world without realizing it.
359 if (mixed_content_node->current_origin().scheme() != url::kHttpsScheme) {
360 mixed_content_features_.insert(
361 MIXED_CONTENT_IN_NON_HTTPS_FRAME_THAT_RESTRICTS_MIXED_CONTENT);
362 }
363 } else if (!SecurityOriginIsSecure(url) &&
364 (IsSecureScheme(root->current_origin().scheme()) ||
365 IsSecureScheme(parent->current_origin().scheme()))) {
366 mixed_content_features_.insert(
367 MIXED_CONTENT_IN_SECURE_FRAME_THAT_DOES_NOT_RESTRICT_MIXED_CONTENT);
368 }
369 return mixed_content_node;
370 }
371
372 void MixedContentNavigationThrottle::MaybeSendBlinkFeatureUsageReport() {
373 if (!mixed_content_features_.empty()) {
374 NavigationHandleImpl* handle_impl =
375 static_cast<NavigationHandleImpl*>(navigation_handle());
376 RenderFrameHost* rfh = handle_impl->frame_tree_node()->current_frame_host();
nasko 2017/01/12 18:32:37 Is the current RFH the correct place to always rep
carlosk 2017/01/21 02:54:58 It shouldn't really matter what frame this is repo
377 rfh->Send(new FrameMsg_BlinkFeatureUsageReport(rfh->GetRoutingID(),
378 mixed_content_features_));
379 mixed_content_features_.clear();
380 }
381 }
382
383 // Based off of MixedContentChecker::count.
384 void MixedContentNavigationThrottle::ReportBasicMixedContentFeatures(
385 RequestContextType request_context_type,
386 blink::WebMixedContentContextType mixed_content_context_type,
387 const WebPreferences& prefs) {
388 mixed_content_features_.insert(MIXED_CONTENT_PRESENT);
389
390 // Report any blockable content.
391 if (mixed_content_context_type ==
392 blink::WebMixedContentContextType::Blockable) {
393 mixed_content_features_.insert(MIXED_CONTENT_BLOCKABLE);
394 return;
395 }
396
397 // Note: as there's no mixed content checks for sub resources on the browser
nasko 2017/01/12 18:32:37 nit: sub-resources or subresources, "browser side"
carlosk 2017/01/21 02:54:58 Done.
398 // there should only be a subset of RequestContextType values that could ever
399 // be found here.
400 UseCounterFeature feature;
401 switch (request_context_type) {
402 case REQUEST_CONTEXT_TYPE_INTERNAL:
403 feature = MIXED_CONTENT_INTERNAL;
404 break;
405 case REQUEST_CONTEXT_TYPE_PREFETCH:
406 feature = MIXED_CONTENT_PREFETCH;
407 break;
408
409 case REQUEST_CONTEXT_TYPE_AUDIO:
410 case REQUEST_CONTEXT_TYPE_DOWNLOAD:
411 case REQUEST_CONTEXT_TYPE_FAVICON:
412 case REQUEST_CONTEXT_TYPE_IMAGE:
413 case REQUEST_CONTEXT_TYPE_PLUGIN:
414 case REQUEST_CONTEXT_TYPE_VIDEO:
415 default:
416 NOTREACHED() << "RequestContextType has value " << request_context_type
417 << " and has WebMixedContentContextType of "
418 << static_cast<int>(mixed_content_context_type);
419 return;
420 }
421 mixed_content_features_.insert(feature);
422 }
423
424 // static
425 bool MixedContentNavigationThrottle::IsMixedContentForTesting(
426 const GURL& origin_url,
427 const GURL& url) {
428 const url::Origin origin(origin_url);
429 return !IsUrlPotentiallySecure(url) &&
430 DoesOriginSchemeRestricsMixedContent(origin);
431 }
432
433 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698