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

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: Overall code cleanup to request reviewers to PTAL. Created 4 years, 5 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 "base/strings/stringprintf.h"
8 #include "content/browser/frame_host/frame_tree.h"
9 #include "content/browser/frame_host/frame_tree_node.h"
10 #include "content/browser/frame_host/navigation_handle_impl.h"
11 #include "content/browser/frame_host/render_frame_host_delegate.h"
12 #include "content/browser/renderer_host/render_view_host_impl.h"
13 #include "content/public/browser/content_browser_client.h"
14 #include "content/public/common/content_client.h"
15 #include "content/public/common/origin_util.h"
16 #include "content/public/common/request_context_type.h"
17 #include "content/public/common/url_constants.h"
18 #include "content/public/common/web_preferences.h"
19 #include "net/base/url_util.h"
20 #include "third_party/WebKit/public/platform/WebMixedContent.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 bool HasPotentiallySecureScheme(const GURL& url) {
31 // TODO(carlosk): find out how to properly handle these secure schemes. Should
32 // statically defined ones in Chrome/embedder come from a shared file? Should
33 // dynamically defined ones from extensions register both with browser and
34 // renderer code? See http://crbug.com/627502.
35 bool result =
36 // Note: schemes statically defined in secureSchemes()
37 url.SchemeIs(url::kHttpsScheme) || url.SchemeIs(url::kAboutScheme) ||
38 url.SchemeIs(url::kDataScheme) || url.SchemeIs(url::kWssScheme) ||
39 // Note: schemes "dynamically" registered with
40 // SchemeRegistry::registerURLSchemeAsSecure.
41 url.SchemeIs(kChromeUIScheme);
42 return result;
43 }
44
45 // Should return the same value as SecurityOrigin::isLocal and
46 // SchemeRegistry::shouldTreatURLSchemeAsLocal.
47 bool HasLocalScheme(const GURL& url) {
48 // TODO(carlosk): Same registration problem as described in
49 // HasPotentiallySecureScheme applies here. See http://crbug.com/627502.
50 bool result =
51 // Note: schemes statically defined in schemesWithUniqueOrigins()
52 url.SchemeIs(url::kAboutScheme) || url.SchemeIs(url::kJavaScriptScheme) ||
53 url.SchemeIs(url::kDataScheme) ||
54 // Note: schemes "dynamically" registered with
55 // SchemeRegistry::registerURLSchemeAsNoAccess. There's no content/
56 // re-definition of kChromeNativeScheme.
57 url.SchemeIs("chrome-native");
carlosk 2016/07/18 14:37:13 This is the kind of hack I'm referring to in my co
58 return result;
59 }
60
61 // Should return the same value as the resource URL checks made inside
62 // MixedContentChecker::isMixedContent.
63 bool IsUrlPotentiallySecure(const GURL& url) {
64 // TODO(carlosk): secure origin checks don't match between content and Blink
65 // hence this implementation here instead of a direct call to IsOriginSecure
66 // (in origin_util.cc). See http://crbug.com/629059.
67 bool is_secure = false;
68
69 // Mimics SecurityOrigin::isSecure.
70 if (HasPotentiallySecureScheme(url) ||
71 (url.SchemeIsFileSystem() &&
72 HasPotentiallySecureScheme(*url.inner_url())) ||
73 (url.SchemeIsBlob() &&
74 HasPotentiallySecureScheme(GURL(url.GetContent()))) ||
75 IsOriginWhiteListedTrustworthy(url)) {
76 is_secure |= true;
77 }
78
79 // Mimics SecurityOrigin::isPotentiallyTrustworthy without duplicating (much)
80 // of the checks already done previously.
81 if (HasLocalScheme(url) || net::IsLocalhost(url.HostNoBrackets()))
82 is_secure |= true;
83
84 // TODO(mkwst): Remove this once 'localhost' is no longer considered
85 // potentially trustworthy.
86 if (is_secure && url.SchemeIs(url::kHttpScheme) &&
87 net::IsLocalHostname(url.HostNoBrackets(), nullptr)) {
88 is_secure = false;
89 }
90
91 return is_secure;
92 }
93
94 // This method should return the same results as
95 // SchemeRegistry::shouldTreatURLSchemeAsRestrictingMixedContent.
96 bool DoesOriginSchemeRestricsMixedContent(const url::Origin& origin) {
97 return origin.scheme() == url::kHttpsScheme;
98 }
99
100 bool ShouldTreatURLSchemeAsCORSEnabled(const GURL& url) {
101 // TODO(carlosk): for CORS schemes we have the exact same issue as for the
102 // secure schemes above. See callers to and references in
103 // WebSecurityPolicy::registerURLSchemeAsCORSEnabled. See
104 // http://crbug.com/627502.
105 return
106 // Note: CORS schemes statically defined in CORSEnabledSchemes()
107 url.SchemeIsHTTPOrHTTPS() || url.SchemeIs(url::kDataScheme) ||
108 // Note: CORS schemes "dynamically" registered in
109 // RenderThreadImpl::RegisterSchemes.
110 url.SchemeIs(kChromeUIScheme);
111 }
112
113 const char* TypeNameFromContext(RequestContextType context) {
114 // Note: the static_cast below is guaranteed by the static asserts in
115 // content/child/web_url_request_util.cc.
116 return blink::WebMixedContent::requestContextName(
117 static_cast<blink::WebURLRequest::RequestContext>(context));
118 }
119
120 blink::WebMixedContent::ContextType MixedContextTypeFromContext(
121 RequestContextType context,
122 bool block_mixed_plugin_content) {
123 // Note: the static_cast below is guaranteed by the static asserts in
124 // content/child/web_url_request_util.cc.
125 return blink::WebMixedContent::contextTypeFromRequestContext(
126 static_cast<blink::WebURLRequest::RequestContext>(context),
127 block_mixed_plugin_content);
128 }
129
130 void LogToConsoleAboutFetch(FrameTreeNode* mixed_content_node,
131 NavigationHandleImpl* handle_impl,
132 const GURL& url,
133 bool allowed) {
134 FrameTreeNode* tree_node = handle_impl->frame_tree_node();
135 // TODO(carlosk): we should only use the very tree_node here for the form
136 // submission case. (D)CHECK for that when we get there. For now just check
137 // this is not the root node.
138 DCHECK(tree_node->parent());
139 RequestContextType request_context_type =
140 handle_impl->fetch_request_context_type();
141
142 std::string message = base::StringPrintf(
143 "Mixed Content: The page at '%s' was loaded over HTTPS, but requested an "
144 "insecure %s '%s'. %s",
145 mixed_content_node->current_url().spec().c_str(),
146 TypeNameFromContext(request_context_type), url.spec().c_str(),
147 allowed ? "This content should also be served over HTTPS."
148 : "This request has been blocked; the content must be served "
149 "over HTTPS.");
150 ConsoleMessageLevel messageLevel =
151 allowed ? ConsoleMessageLevel::CONSOLE_MESSAGE_LEVEL_WARNING
152 : ConsoleMessageLevel::CONSOLE_MESSAGE_LEVEL_ERROR;
153 tree_node->current_frame_host()->AddMessageToConsole(messageLevel, message);
154 }
155
156 } // namespace
157
158 namespace content {
159
160 MixedContentNavigationThrottle::MixedContentNavigationThrottle(
161 NavigationHandle* navigation_handle)
162 : NavigationThrottle(navigation_handle) {}
163
164 MixedContentNavigationThrottle::~MixedContentNavigationThrottle() {}
165
166 ThrottleCheckResult MixedContentNavigationThrottle::WillStartRequest() {
167 NavigationHandleImpl* handle_impl =
168 static_cast<NavigationHandleImpl*>(navigation_handle());
169 FrameTreeNode* node = handle_impl->frame_tree_node();
170
171 // Find the parent node with mixed content, if any.
172 FrameTreeNode* mixed_content_node =
173 InWhichFrameIsContentMixed(node, navigation_handle()->GetURL());
174 if (!mixed_content_node)
175 return ThrottleCheckResult::PROCEED;
176
177 // From this point on we know this is not a main frame navigation and that
178 // there is mixed-content. Now let's decide if it's OK to proceed with it.
179
180 const WebPreferences& prefs =
181 node->current_frame_host()->render_view_host()->GetWebkitPreferences();
182
183 // If we're in strict mode, we'll automagically fail everything, and
184 // intentionally skip the client/embedder checks in order to prevent degrading
185 // the site's security UI.
186 bool block_all_mixed_content = !!(
187 mixed_content_node->current_replication_state().insecure_request_policy &
188 blink::kBlockAllMixedContent);
189 bool strictMode =
190 prefs.strict_mixed_content_checking || block_all_mixed_content;
191
192 blink::WebMixedContent::ContextType mixed_context_type =
193 MixedContextTypeFromContext(handle_impl->fetch_request_context_type(),
194 prefs.block_mixed_plugin_content);
195
196 if (!ShouldTreatURLSchemeAsCORSEnabled(navigation_handle()->GetURL())) {
197 mixed_context_type =
198 blink::WebMixedContent::ContextType::OptionallyBlockable;
199 }
200
201 bool allowed = false;
202 ContentBrowserClient* browser_client = GetContentClient()->browser();
203 RenderFrameHostDelegate* frame_host_delegate =
204 node->current_frame_host()->delegate();
205 switch (mixed_context_type) {
206 case blink::WebMixedContent::ContextType::OptionallyBlockable:
207 allowed =
208 !strictMode &&
209 browser_client->ShouldAllowDisplayingInsecureContent(
210 prefs.allow_displaying_insecure_content,
211 navigation_handle()->GetURL(), handle_impl->GetWebContents());
212 if (allowed)
213 frame_host_delegate->DidDisplayInsecureContent();
214 break;
215
216 case blink::WebMixedContent::ContextType::Blockable: {
217 bool shouldAskEmbedder =
218 !strictMode && (!prefs.strictly_block_blockable_mixed_content ||
219 prefs.allow_running_insecure_content);
220 allowed =
221 shouldAskEmbedder &&
222 browser_client->ShouldAllowRunningInsecureContent(
223 prefs.allow_running_insecure_content,
224 mixed_content_node->current_origin(),
225 navigation_handle()->GetURL(), handle_impl->GetWebContents());
226 if (allowed) {
227 const GURL& origin_url = mixed_content_node->current_url().GetOrigin();
228 frame_host_delegate->DidRunInsecureContent(
229 origin_url, navigation_handle()->GetURL());
230 browser_client->RecordURLMetric(
231 "ContentSettings.MixedScript.RanMixedScript", origin_url);
232 }
233 break;
234 }
235
236 case blink::WebMixedContent::ContextType::ShouldBeBlockable:
237 allowed = !strictMode;
238 if (allowed)
239 frame_host_delegate->DidDisplayInsecureContent();
240 break;
241
242 case blink::WebMixedContent::ContextType::NotMixedContent:
243 NOTREACHED();
244 break;
245 };
246
247 LogToConsoleAboutFetch(mixed_content_node, handle_impl,
248 navigation_handle()->GetURL(), allowed);
carlosk 2016/07/18 14:37:13 Following the implementation of MixedContentChecke
249
250 return allowed ? ThrottleCheckResult::PROCEED : ThrottleCheckResult::CANCEL;
251 }
252
253 ThrottleCheckResult MixedContentNavigationThrottle::WillRedirectRequest() {
254 // Upon redirects the same checks are to be executed as for requests.
255 return MixedContentNavigationThrottle::WillStartRequest();
256 }
257
258 ThrottleCheckResult MixedContentNavigationThrottle::WillProcessResponse() {
259 // TODO(carlosk): at this point we must check the final security level of
260 // the connection! Does it use an outdated protocol? See
261 // MixedContentChecker::handleCertificateError
262 return ThrottleCheckResult::PROCEED;
263 }
264
265 FrameTreeNode* MixedContentNavigationThrottle::InWhichFrameIsContentMixed(
266 FrameTreeNode* node,
267 const GURL& url) {
268 // Main frame navigations cannot be mixed content.
269 // TODO(carlosk): except for form submissions which will be dealt with later.
270 if (node->IsMainFrame())
271 return nullptr;
272
273 // If the navigated URL is potentially secure there's no mixed-content (at
274 // request time at least).
275 if (IsUrlPotentiallySecure(url))
276 return nullptr;
277
278 // TODO(carlosk): don't we need to check more than just the immediate parent
279 // and the root? Is it always the case that these two are the only sources for
280 // obtaining the "origin of the security context"? http://crbug.com/623486
281
282 // If neither the parent nor root frames' origins are secure, there is no
283 // mixed content.
284 FrameTreeNode* mixed_content_node = nullptr;
285
286 // Checks if the root or immediate parent frame's origin are secure.
287 FrameTreeNode* root = node->frame_tree()->root();
288 FrameTreeNode* parent = node->parent();
289 if (DoesOriginSchemeRestricsMixedContent(root->current_origin()))
290 mixed_content_node = root;
291 else if (DoesOriginSchemeRestricsMixedContent(parent->current_origin()))
292 mixed_content_node = parent;
293
294 return mixed_content_node;
295 }
296
297 // static
298 bool MixedContentNavigationThrottle::IsMixedContentForTesting(
299 const GURL& origin_url,
300 const GURL& url) {
301 const url::Origin origin(origin_url);
302 return !IsUrlPotentiallySecure(url) &&
303 DoesOriginSchemeRestricsMixedContent(origin);
304 }
305
306 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698