OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012 Google Inc. All rights reserved. | 2 * Copyright (C) 2012 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 26 matching lines...) Expand all Loading... |
37 #include "core/loader/FrameLoader.h" | 37 #include "core/loader/FrameLoader.h" |
38 #include "core/loader/FrameLoaderClient.h" | 38 #include "core/loader/FrameLoaderClient.h" |
39 #include "platform/RuntimeEnabledFeatures.h" | 39 #include "platform/RuntimeEnabledFeatures.h" |
40 #include "platform/weborigin/SchemeRegistry.h" | 40 #include "platform/weborigin/SchemeRegistry.h" |
41 #include "platform/weborigin/SecurityOrigin.h" | 41 #include "platform/weborigin/SecurityOrigin.h" |
42 #include "public/platform/Platform.h" | 42 #include "public/platform/Platform.h" |
43 #include "wtf/text/StringBuilder.h" | 43 #include "wtf/text/StringBuilder.h" |
44 | 44 |
45 namespace blink { | 45 namespace blink { |
46 | 46 |
47 namespace { | |
48 } // namespace | |
49 | |
50 MixedContentChecker::MixedContentChecker(LocalFrame* frame) | |
51 : m_frame(frame) | |
52 { | |
53 } | |
54 | |
55 FrameLoaderClient* MixedContentChecker::client() const | |
56 { | |
57 return m_frame->loader().client(); | |
58 } | |
59 | |
60 // static | 47 // static |
61 bool MixedContentChecker::isMixedContent(SecurityOrigin* securityOrigin, const K
URL& url) | 48 bool MixedContentChecker::isMixedContent(SecurityOrigin* securityOrigin, const K
URL& url) |
62 { | 49 { |
63 if (securityOrigin->protocol() != "https") | 50 if (securityOrigin->protocol() != "https") |
64 return false; // We only care about HTTPS security origins. | 51 return false; // We only care about HTTPS security origins. |
65 | 52 |
66 // We're in a secure context, so |url| is mixed content if it's insecure. | 53 // We're in a secure context, so |url| is mixed content if it's insecure. |
67 return !SecurityOrigin::isSecure(url); | 54 return !SecurityOrigin::isSecure(url); |
68 } | 55 } |
69 | 56 |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
196 case WebURLRequest::RequestContextXSLT: | 183 case WebURLRequest::RequestContextXSLT: |
197 return "XSLT"; | 184 return "XSLT"; |
198 } | 185 } |
199 ASSERT_NOT_REACHED(); | 186 ASSERT_NOT_REACHED(); |
200 return "resource"; | 187 return "resource"; |
201 } | 188 } |
202 | 189 |
203 // static | 190 // static |
204 void MixedContentChecker::logToConsole(LocalFrame* frame, const KURL& url, WebUR
LRequest::RequestContext requestContext, bool allowed) | 191 void MixedContentChecker::logToConsole(LocalFrame* frame, const KURL& url, WebUR
LRequest::RequestContext requestContext, bool allowed) |
205 { | 192 { |
206 String message = String::format( | |
207 "Mixed Content: The page at '%s' was loaded over HTTPS, but requested an
insecure %s '%s'. %s", | |
208 frame->document()->url().elidedString().utf8().data(), typeNameFromConte
xt(requestContext), url.elidedString().utf8().data(), | |
209 allowed ? "This content should also be served over HTTPS." : "This reque
st has been blocked; the content must be served over HTTPS."); | |
210 MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLeve
l; | |
211 frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageS
ource, messageLevel, message)); | |
212 } | 193 } |
213 | 194 |
214 // static | 195 LocalFrame* MixedContentChecker::inWhichFrameIsThisContentMixed(LocalFrame* fram
e, WebURLRequest::RequestContext requestContext, WebURLRequest::FrameType frameT
ype, const KURL& url) |
215 bool MixedContentChecker::shouldBlockFetch(LocalFrame* frame, const ResourceRequ
est& resourceRequest, const KURL& url) | |
216 { | 196 { |
217 // No frame, no mixed content: | 197 // No frame, no mixed content: |
218 if (!frame) | 198 if (!frame) |
219 return false; | 199 return 0; |
| 200 |
| 201 // We only care about subresource loads; top-level navigations cannot be mix
ed content. |
| 202 if (frameType == WebURLRequest::FrameTypeTopLevel) |
| 203 return 0; |
220 | 204 |
221 // Check the top frame first. | 205 // Check the top frame first. |
222 if (Frame* top = frame->tree().top()) { | 206 if (Frame* top = frame->tree().top()) { |
223 // FIXME: We need a way to access the top-level frame's SecurityOrigin w
hen that frame | 207 // FIXME: We need a way to access the top-level frame's SecurityOrigin w
hen that frame |
224 // is in a different process from the current frame. Until that is done,
we bail out | 208 // is in a different process from the current frame. Until that is done,
we bail out |
225 // early and allow the load. | 209 // early and allow the load. |
226 if (!top->isLocalFrame()) | 210 if (!top->isLocalFrame()) |
227 return false; | 211 return 0; |
228 | 212 |
229 LocalFrame* localTop = toLocalFrame(top); | 213 LocalFrame* localTop = toLocalFrame(top); |
230 if (frame != localTop && shouldBlockFetch(localTop, resourceRequest, url
)) | 214 if (frame != localTop && inWhichFrameIsThisContentMixed(localTop, reques
tContext, frameType, url)) |
231 return true; | 215 return localTop; |
232 } | 216 } |
233 | 217 |
234 // We only care about subresource loads; top-level navigations cannot be mix
ed content. | 218 // Just count these for the moment, don't block them. |
235 if (resourceRequest.frameType() == WebURLRequest::FrameTypeTopLevel) | 219 if (Platform::current()->isReservedIPAddress(url) && !Platform::current()->i
sReservedIPAddress(KURL(ParsedURLString, frame->document()->securityOrigin()->to
String()))) |
236 return false; | 220 UseCounter::count(frame->document(), contextTypeFromContext(requestConte
xt) == ContextTypeBlockable ? UseCounter::MixedContentPrivateIPInPublicWebsiteAc
tive : UseCounter::MixedContentPrivateIPInPublicWebsitePassive); |
237 | 221 |
238 // No mixed content, no problem. | 222 // No mixed content, no problem. |
239 if (!isMixedContent(frame->document()->securityOrigin(), url)) | 223 if (!isMixedContent(frame->document()->securityOrigin(), url)) |
| 224 return 0; |
| 225 |
| 226 return frame; |
| 227 } |
| 228 |
| 229 // static |
| 230 bool MixedContentChecker::shouldBlockFetch(LocalFrame* frame, WebURLRequest::Req
uestContext requestContext, WebURLRequest::FrameType frameType, const KURL& url) |
| 231 { |
| 232 LocalFrame* effectiveFrame = inWhichFrameIsThisContentMixed(frame, requestCo
ntext, frameType, url); |
| 233 if (!effectiveFrame) |
240 return false; | 234 return false; |
241 | 235 |
242 Settings* settings = frame->settings(); | 236 Settings* settings = effectiveFrame->settings(); |
243 FrameLoaderClient* client = frame->loader().client(); | 237 FrameLoaderClient* client = effectiveFrame->loader().client(); |
244 SecurityOrigin* securityOrigin = frame->document()->securityOrigin(); | 238 SecurityOrigin* securityOrigin = effectiveFrame->document()->securityOrigin(
); |
245 bool allowed = false; | 239 bool allowed = false; |
246 | 240 |
247 ContextType contextType = contextTypeFromContext(resourceRequest.requestCont
ext()); | 241 ContextType contextType = contextTypeFromContext(requestContext); |
248 if (contextType == ContextTypeBlockableUnlessLax) | 242 if (contextType == ContextTypeBlockableUnlessLax) |
249 contextType = RuntimeEnabledFeatures::laxMixedContentCheckingEnabled() ?
ContextTypeOptionallyBlockable : ContextTypeBlockable; | 243 contextType = RuntimeEnabledFeatures::laxMixedContentCheckingEnabled() ?
ContextTypeOptionallyBlockable : ContextTypeBlockable; |
250 | 244 |
| 245 if (frameType == WebURLRequest::FrameTypeNested) { |
| 246 // For subframes, if we're dealing with a CORS-enabled scheme, then bloc
k mixed content. Otherwise, |
| 247 // treat frames as passive content. |
| 248 // |
| 249 // FIXME: Remove this temporary hack once we have a reasonable API for l
aunching external applications |
| 250 // via URLs. http://crbug.com/318788 and https://crbug.com/393481 |
| 251 contextType = SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(url.prot
ocol()) ? ContextTypeBlockable : ContextTypeOptionallyBlockable; |
| 252 } |
| 253 |
251 switch (contextType) { | 254 switch (contextType) { |
252 case ContextTypeOptionallyBlockable: | 255 case ContextTypeOptionallyBlockable: |
253 allowed = client->allowDisplayingInsecureContent(settings && settings->a
llowDisplayOfInsecureContent(), securityOrigin, url); | 256 allowed = client->allowDisplayingInsecureContent(settings && settings->a
llowDisplayOfInsecureContent(), securityOrigin, url); |
254 if (allowed) | 257 if (allowed) |
255 client->didDisplayInsecureContent(); | 258 client->didDisplayInsecureContent(); |
256 break; | 259 break; |
257 | 260 |
258 case ContextTypeBlockable: | 261 case ContextTypeBlockable: |
259 allowed = client->allowRunningInsecureContent(settings && settings->allo
wRunningOfInsecureContent(), securityOrigin, url); | 262 allowed = client->allowRunningInsecureContent(settings && settings->allo
wRunningOfInsecureContent(), securityOrigin, url); |
260 if (allowed) | 263 if (allowed) |
261 client->didRunInsecureContent(securityOrigin, url); | 264 client->didRunInsecureContent(securityOrigin, url); |
262 break; | 265 break; |
263 | 266 |
264 case ContextTypeShouldBeBlockable: | 267 case ContextTypeShouldBeBlockable: |
265 return false; | 268 return false; |
266 | 269 |
267 case ContextTypeBlockableUnlessLax: | 270 case ContextTypeBlockableUnlessLax: |
268 // We map this to either OptionallyBlockable or Blockable above. | 271 // We map this to either OptionallyBlockable or Blockable above. |
269 ASSERT_NOT_REACHED(); | 272 ASSERT_NOT_REACHED(); |
270 return true; | 273 return true; |
271 }; | 274 }; |
272 | 275 |
273 logToConsole(frame, url, resourceRequest.requestContext(), allowed); | 276 String message = String::format( |
| 277 "Mixed Content: The page at '%s' was loaded over HTTPS, but requested an
insecure %s '%s'. %s", |
| 278 frame->document()->url().elidedString().utf8().data(), typeNameFromConte
xt(requestContext), url.elidedString().utf8().data(), |
| 279 allowed ? "This content should also be served over HTTPS." : "This reque
st has been blocked; the content must be served over HTTPS."); |
| 280 MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLeve
l; |
| 281 frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageS
ource, messageLevel, message)); |
| 282 |
274 return !allowed; | 283 return !allowed; |
275 } | 284 } |
276 | 285 |
277 bool MixedContentChecker::canDisplayInsecureContentInternal(SecurityOrigin* secu
rityOrigin, const KURL& url, const MixedContentType type) const | 286 // static |
| 287 bool MixedContentChecker::shouldBlockWebSocket(LocalFrame* frame, const KURL& ur
l) |
278 { | 288 { |
279 // Check the top frame if it differs from MixedContentChecker's m_frame. | 289 // FIXME: Should we have a RequestContext for WebSockets? Probably not, but
this feels hacky otherwise. |
280 if (!m_frame->tree().top()->isLocalFrame()) { | 290 LocalFrame* effectiveFrame = inWhichFrameIsThisContentMixed(frame, WebURLReq
uest::RequestContextXMLHttpRequest, WebURLRequest::FrameTypeNone, url); |
281 // FIXME: We need a way to access the top-level frame's MixedContentChec
ker when that frame | 291 if (!effectiveFrame) |
282 // is in a different process from the current frame. Until that is done,
we always allow | |
283 // loads in remote frames. | |
284 return false; | |
285 } | |
286 Frame* top = m_frame->tree().top(); | |
287 if (top != m_frame && !toLocalFrame(top)->loader().mixedContentChecker()->ca
nDisplayInsecureContent(toLocalFrame(top)->document()->securityOrigin(), url)) | |
288 return false; | 292 return false; |
289 | 293 |
290 // Just count these for the moment, don't block them. | 294 Settings* settings = effectiveFrame->settings(); |
291 if (Platform::current()->isReservedIPAddress(url) && !Platform::current()->i
sReservedIPAddress(KURL(ParsedURLString, securityOrigin->toString()))) | 295 FrameLoaderClient* client = effectiveFrame->loader().client(); |
292 UseCounter::count(m_frame->document(), UseCounter::MixedContentPrivateIP
InPublicWebsitePassive); | 296 SecurityOrigin* securityOrigin = effectiveFrame->document()->securityOrigin(
); |
| 297 bool allowed = false; |
293 | 298 |
294 // Then check the current frame: | 299 if (RuntimeEnabledFeatures::laxMixedContentCheckingEnabled()) { |
295 if (!isMixedContent(securityOrigin, url)) | 300 allowed = client->allowDisplayingInsecureContent(settings && (settings->
allowRunningOfInsecureContent() || settings->allowConnectingInsecureWebSocket())
, securityOrigin, url); |
296 return true; | 301 if (allowed) |
| 302 client->didDisplayInsecureContent(); |
| 303 } else { |
| 304 allowed = client->allowRunningInsecureContent(settings && (settings->all
owRunningOfInsecureContent() || settings->allowConnectingInsecureWebSocket()), s
ecurityOrigin, url); |
| 305 if (allowed) |
| 306 client->didRunInsecureContent(securityOrigin, url); |
| 307 } |
297 | 308 |
298 Settings* settings = m_frame->settings(); | 309 String message = String::format( |
299 bool allowed = client()->allowDisplayingInsecureContent(settings && settings
->allowDisplayOfInsecureContent(), securityOrigin, url); | 310 "Mixed Content: The page at '%s' was loaded over HTTPS, but requested th
e WebSocket endpoint '%s'. %s", |
300 logWarning(allowed, url, type); | 311 effectiveFrame->document()->url().elidedString().utf8().data(), url.elid
edString().utf8().data(), |
| 312 allowed ? "This endpoint should also be secure ('wss:')." : "This reques
t has been blocked; the endpoint must be secure ('wss:')."); |
| 313 MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLeve
l; |
| 314 effectiveFrame->document()->addConsoleMessage(ConsoleMessage::create(Securit
yMessageSource, messageLevel, message)); |
301 | 315 |
302 if (allowed) | 316 return !allowed; |
303 client()->didDisplayInsecureContent(); | |
304 | |
305 return allowed; | |
306 } | 317 } |
307 | 318 |
308 bool MixedContentChecker::canRunInsecureContentInternal(SecurityOrigin* security
Origin, const KURL& url, const MixedContentType type) const | 319 // static |
309 { | 320 bool MixedContentChecker::checkFormAction(LocalFrame* frame, const KURL& url) |
310 // Check the top frame if it differs from MixedContentChecker's m_frame. | |
311 if (!m_frame->tree().top()->isLocalFrame()) { | |
312 // FIXME: We need a way to access the top-level frame's MixedContentChec
ker when that frame | |
313 // is in a different process from the current frame. Until that is done,
we always allow | |
314 // loads in remote frames. | |
315 return true; | |
316 } | |
317 Frame* top = m_frame->tree().top(); | |
318 if (top != m_frame && !toLocalFrame(top)->loader().mixedContentChecker()->ca
nRunInsecureContent(toLocalFrame(top)->document()->securityOrigin(), url)) | |
319 return false; | |
320 | |
321 // Just count these for the moment, don't block them. | |
322 if (Platform::current()->isReservedIPAddress(url) && !Platform::current()->i
sReservedIPAddress(KURL(ParsedURLString, securityOrigin->toString()))) | |
323 UseCounter::count(m_frame->document(), UseCounter::MixedContentPrivateIP
InPublicWebsiteActive); | |
324 | |
325 // Then check the current frame: | |
326 if (!isMixedContent(securityOrigin, url)) | |
327 return true; | |
328 | |
329 Settings* settings = m_frame->settings(); | |
330 bool allowedPerSettings = settings && (settings->allowRunningOfInsecureConte
nt() || ((type == WebSocket) && settings->allowConnectingInsecureWebSocket())); | |
331 bool allowed = client()->allowRunningInsecureContent(allowedPerSettings, sec
urityOrigin, url); | |
332 logWarning(allowed, url, type); | |
333 | |
334 if (allowed) | |
335 client()->didRunInsecureContent(securityOrigin, url); | |
336 | |
337 return allowed; | |
338 } | |
339 | |
340 bool MixedContentChecker::canFrameInsecureContent(SecurityOrigin* securityOrigin
, const KURL& url) const | |
341 { | |
342 // If we're dealing with a CORS-enabled scheme, then block mixed frames as a
ctive content. Otherwise, | |
343 // treat frames as passive content. | |
344 // | |
345 // FIXME: Remove this temporary hack once we have a reasonable API for launc
hing external applications | |
346 // via URLs. http://crbug.com/318788 and https://crbug.com/393481 | |
347 if (SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(url.protocol())) | |
348 return canRunInsecureContentInternal(securityOrigin, url, MixedContentCh
ecker::Execution); | |
349 return canDisplayInsecureContentInternal(securityOrigin, url, MixedContentCh
ecker::Display); | |
350 } | |
351 | |
352 bool MixedContentChecker::canConnectInsecureWebSocket(SecurityOrigin* securityOr
igin, const KURL& url) const | |
353 { | |
354 if (RuntimeEnabledFeatures::laxMixedContentCheckingEnabled()) | |
355 return canDisplayInsecureContentInternal(securityOrigin, url, MixedConte
ntChecker::WebSocket); | |
356 return canRunInsecureContentInternal(securityOrigin, url, MixedContentChecke
r::WebSocket); | |
357 } | |
358 | |
359 bool MixedContentChecker::canSubmitToInsecureForm(SecurityOrigin* securityOrigin
, const KURL& url) const | |
360 { | 321 { |
361 // For whatever reason, some folks handle forms via JavaScript, and submit t
o `javascript:void(0)` | 322 // For whatever reason, some folks handle forms via JavaScript, and submit t
o `javascript:void(0)` |
362 // rather than calling `preventDefault()`. We special-case `javascript:` URL
s here, as they don't | 323 // rather than calling `preventDefault()`. We special-case `javascript:` URL
s here, as they don't |
363 // introduce MixedContent for form submissions. | 324 // introduce MixedContent for form submissions. |
364 if (url.protocolIs("javascript")) | 325 if (url.protocolIs("javascript")) |
365 return true; | 326 return false; |
366 | 327 |
367 // If lax mixed content checking is enabled (noooo!), skip this check entire
ly. | 328 // If lax mixed content checking is enabled (noooo!), skip this check entire
ly. |
368 if (RuntimeEnabledFeatures::laxMixedContentCheckingEnabled()) | 329 if (RuntimeEnabledFeatures::laxMixedContentCheckingEnabled()) |
369 return true; | 330 return false; |
370 return canDisplayInsecureContentInternal(securityOrigin, url, MixedContentCh
ecker::Submission); | |
371 } | |
372 | 331 |
373 void MixedContentChecker::logWarning(bool allowed, const KURL& target, const Mix
edContentType type) const | 332 LocalFrame* effectiveFrame = inWhichFrameIsThisContentMixed(frame, WebURLReq
uest::RequestContextForm, WebURLRequest::FrameTypeNone, url); |
374 { | 333 if (!effectiveFrame) |
375 StringBuilder message; | 334 return false; |
376 message.append((allowed ? "" : "[blocked] ")); | 335 |
377 message.append("The page at '" + m_frame->document()->url().elidedString() +
"' was loaded over HTTPS, but "); | 336 // No "allowed" check here; we're not yet exposing anything which would bloc
k form submission. |
378 switch (type) { | 337 FrameLoaderClient* client = effectiveFrame->loader().client(); |
379 case Display: | 338 client->didDisplayInsecureContent(); |
380 message.append("displayed insecure content from '" + target.elidedString
() + "': this content should also be loaded over HTTPS.\n"); | 339 |
381 break; | 340 String message = String::format( |
382 case Execution: | 341 "Mixed Content: The page at '%s' was loaded over HTTPS, but contains a f
orm whose 'action' attribute is '%s'. This form should not submit data to insecu
re endpoints.", |
383 case WebSocket: | 342 effectiveFrame->document()->url().elidedString().utf8().data(), url.elid
edString().utf8().data()); |
384 message.append("ran insecure content from '" + target.elidedString() + "
': this content should also be loaded over HTTPS.\n"); | 343 effectiveFrame->document()->addConsoleMessage(ConsoleMessage::create(Securit
yMessageSource, WarningMessageLevel, message)); |
385 break; | 344 return true; |
386 case Submission: | |
387 message.append("is submitting data to an insecure location at '" + targe
t.elidedString() + "': this content should also be submitted over HTTPS.\n"); | |
388 break; | |
389 } | |
390 MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLeve
l; | |
391 m_frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessag
eSource, messageLevel, message.toString())); | |
392 } | 345 } |
393 | 346 |
394 } // namespace blink | 347 } // namespace blink |
OLD | NEW |