Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / loader / MixedContentChecker.cpp
1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "core/loader/MixedContentChecker.h"
31
32 #include "core/dom/Document.h"
33 #include "core/frame/LocalFrame.h"
34 #include "core/frame/Settings.h"
35 #include "core/frame/UseCounter.h"
36 #include "core/inspector/ConsoleMessage.h"
37 #include "core/loader/DocumentLoader.h"
38 #include "core/loader/FrameLoader.h"
39 #include "core/loader/FrameLoaderClient.h"
40 #include "platform/RuntimeEnabledFeatures.h"
41 #include "platform/weborigin/SchemeRegistry.h"
42 #include "platform/weborigin/SecurityOrigin.h"
43 #include "public/platform/Platform.h"
44 #include "wtf/text/StringBuilder.h"
45
46 namespace blink {
47
48 namespace {
49 } // namespace
50
51 MixedContentChecker::MixedContentChecker(LocalFrame* frame)
52     : m_frame(frame)
53 {
54 }
55
56 FrameLoaderClient* MixedContentChecker::client() const
57 {
58     return m_frame->loader().client();
59 }
60
61 // static
62 bool MixedContentChecker::isMixedContent(SecurityOrigin* securityOrigin, const KURL& url)
63 {
64     if (securityOrigin->protocol() != "https")
65         return false; // We only care about HTTPS security origins.
66
67     // We're in a secure context, so |url| is mixed content if it's insecure.
68     return !SecurityOrigin::isSecure(url);
69 }
70
71 // static
72 MixedContentChecker::ContextType MixedContentChecker::contextTypeFromContext(WebURLRequest::RequestContext context)
73 {
74     switch (context) {
75     // "Optionally-blockable" mixed content
76     case WebURLRequest::RequestContextAudio:
77     case WebURLRequest::RequestContextFavicon:
78     case WebURLRequest::RequestContextImage:
79     case WebURLRequest::RequestContextVideo:
80         return ContextTypeOptionallyBlockable;
81
82     // "Blockable" mixed content
83     case WebURLRequest::RequestContextBeacon:
84     case WebURLRequest::RequestContextCSPReport:
85     case WebURLRequest::RequestContextEmbed:
86     case WebURLRequest::RequestContextFetch:
87     case WebURLRequest::RequestContextFont:
88     case WebURLRequest::RequestContextForm:
89     case WebURLRequest::RequestContextFrame:
90     case WebURLRequest::RequestContextHyperlink:
91     case WebURLRequest::RequestContextIframe:
92     case WebURLRequest::RequestContextImageSet:
93     case WebURLRequest::RequestContextImport:
94     case WebURLRequest::RequestContextLocation:
95     case WebURLRequest::RequestContextManifest:
96     case WebURLRequest::RequestContextObject:
97     case WebURLRequest::RequestContextPing:
98     case WebURLRequest::RequestContextScript:
99     case WebURLRequest::RequestContextServiceWorker:
100     case WebURLRequest::RequestContextSharedWorker:
101     case WebURLRequest::RequestContextStyle:
102     case WebURLRequest::RequestContextSubresource:
103     case WebURLRequest::RequestContextTrack:
104     case WebURLRequest::RequestContextWorker:
105     case WebURLRequest::RequestContextXSLT:
106         return ContextTypeBlockable;
107
108     // "Blockable" mixed content whose behavior changed recently, and which is thus guarded behind the "lax" flag
109     case WebURLRequest::RequestContextEventSource:
110     case WebURLRequest::RequestContextXMLHttpRequest:
111         return ContextTypeBlockableUnlessLax;
112
113     // FIXME: Contexts that we should block, but don't currently. https://crbug.com/388650
114     case WebURLRequest::RequestContextDownload:
115     case WebURLRequest::RequestContextInternal:
116     case WebURLRequest::RequestContextPlugin:
117     case WebURLRequest::RequestContextPrefetch:
118         return ContextTypeShouldBeBlockable;
119
120     case WebURLRequest::RequestContextUnspecified:
121         ASSERT_NOT_REACHED();
122     }
123     ASSERT_NOT_REACHED();
124     return ContextTypeBlockable;
125 }
126
127 // static
128 const char* MixedContentChecker::typeNameFromContext(WebURLRequest::RequestContext context)
129 {
130     switch (context) {
131     case WebURLRequest::RequestContextAudio:
132         return "audio file";
133     case WebURLRequest::RequestContextBeacon:
134         return "Beacon endpoint";
135     case WebURLRequest::RequestContextCSPReport:
136         return "Content Security Policy reporting endpoint";
137     case WebURLRequest::RequestContextDownload:
138         return "download";
139     case WebURLRequest::RequestContextEmbed:
140         return "plugin resource";
141     case WebURLRequest::RequestContextEventSource:
142         return "EventSource endpoint";
143     case WebURLRequest::RequestContextFavicon:
144         return "favicon";
145     case WebURLRequest::RequestContextFetch:
146         return "resource";
147     case WebURLRequest::RequestContextFont:
148         return "font";
149     case WebURLRequest::RequestContextForm:
150         return "form action";
151     case WebURLRequest::RequestContextFrame:
152         return "frame";
153     case WebURLRequest::RequestContextHyperlink:
154         return "resource";
155     case WebURLRequest::RequestContextIframe:
156         return "frame";
157     case WebURLRequest::RequestContextImage:
158         return "image";
159     case WebURLRequest::RequestContextImageSet:
160         return "image";
161     case WebURLRequest::RequestContextImport:
162         return "HTML Import";
163     case WebURLRequest::RequestContextInternal:
164         return "resource";
165     case WebURLRequest::RequestContextLocation:
166         return "resource";
167     case WebURLRequest::RequestContextManifest:
168         return "manifest";
169     case WebURLRequest::RequestContextObject:
170         return "plugin resource";
171     case WebURLRequest::RequestContextPing:
172         return "hyperlink auditing endpoint";
173     case WebURLRequest::RequestContextPlugin:
174         return "plugin data";
175     case WebURLRequest::RequestContextPrefetch:
176         return "prefetch resource";
177     case WebURLRequest::RequestContextScript:
178         return "script";
179     case WebURLRequest::RequestContextServiceWorker:
180         return "Service Worker script";
181     case WebURLRequest::RequestContextSharedWorker:
182         return "Shared Worker script";
183     case WebURLRequest::RequestContextStyle:
184         return "stylesheet";
185     case WebURLRequest::RequestContextSubresource:
186         return "resource";
187     case WebURLRequest::RequestContextTrack:
188         return "Text Track";
189     case WebURLRequest::RequestContextUnspecified:
190         return "resource";
191     case WebURLRequest::RequestContextVideo:
192         return "video";
193     case WebURLRequest::RequestContextWorker:
194         return "Worker script";
195     case WebURLRequest::RequestContextXMLHttpRequest:
196         return "XMLHttpRequest endpoint";
197     case WebURLRequest::RequestContextXSLT:
198         return "XSLT";
199     }
200     ASSERT_NOT_REACHED();
201     return "resource";
202 }
203
204 // static
205 void MixedContentChecker::logToConsole(LocalFrame* frame, const KURL& url, WebURLRequest::RequestContext requestContext, bool allowed)
206 {
207     String message = String::format(
208         "Mixed Content: The page at '%s' was loaded over HTTPS, but requested an insecure %s '%s'. %s",
209         frame->document()->url().elidedString().utf8().data(), typeNameFromContext(requestContext), url.elidedString().utf8().data(),
210         allowed ? "This content should also be served over HTTPS." : "This request has been blocked; the content must be served over HTTPS.");
211     MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLevel;
212     frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, messageLevel, message));
213 }
214
215 // static
216 bool MixedContentChecker::shouldBlockFetch(LocalFrame* frame, const ResourceRequest& resourceRequest, const KURL& url)
217 {
218     // No frame, no mixed content:
219     if (!frame)
220         return false;
221
222     // Check the top frame first.
223     if (Frame* top = frame->tree().top()) {
224         // FIXME: We need a way to access the top-level frame's SecurityOrigin when that frame
225         // is in a different process from the current frame. Until that is done, we bail out
226         // early and allow the load.
227         if (!top->isLocalFrame())
228             return false;
229
230         LocalFrame* localTop = toLocalFrame(top);
231         if (frame != localTop && shouldBlockFetch(localTop, resourceRequest, url))
232             return true;
233     }
234
235     // We only care about subresource loads; top-level navigations cannot be mixed content.
236     if (resourceRequest.frameType() == WebURLRequest::FrameTypeTopLevel)
237         return false;
238
239     // No mixed content, no problem.
240     if (!isMixedContent(frame->document()->securityOrigin(), url))
241         return false;
242
243     Settings* settings = frame->settings();
244     FrameLoaderClient* client = frame->loader().client();
245     SecurityOrigin* securityOrigin = frame->document()->securityOrigin();
246     bool allowed = false;
247
248     ContextType contextType = contextTypeFromContext(resourceRequest.requestContext());
249     if (contextType == ContextTypeBlockableUnlessLax)
250         contextType = RuntimeEnabledFeatures::laxMixedContentCheckingEnabled() ? ContextTypeOptionallyBlockable : ContextTypeBlockable;
251
252     // If we're loading the main resource of a subframe, we need to take a close look at the loaded URL.
253     // If we're dealing with a CORS-enabled scheme, then block mixed frames as active content. Otherwise,
254     // treat frames as passive content.
255     //
256     // FIXME: Remove this temporary hack once we have a reasonable API for launching external applications
257     // via URLs. http://crbug.com/318788 and https://crbug.com/393481
258     if (resourceRequest.frameType() == WebURLRequest::FrameTypeNested && !SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(url.protocol()))
259         contextType = ContextTypeOptionallyBlockable;
260
261     switch (contextType) {
262     case ContextTypeOptionallyBlockable:
263         allowed = client->allowDisplayingInsecureContent(settings && settings->allowDisplayOfInsecureContent(), securityOrigin, url);
264         if (allowed)
265             client->didDisplayInsecureContent();
266         break;
267
268     case ContextTypeBlockable:
269         allowed = client->allowRunningInsecureContent(settings && settings->allowRunningOfInsecureContent(), securityOrigin, url);
270         if (allowed)
271             client->didRunInsecureContent(securityOrigin, url);
272         break;
273
274     case ContextTypeShouldBeBlockable:
275         return false;
276
277     case ContextTypeBlockableUnlessLax:
278         // We map this to either OptionallyBlockable or Blockable above.
279         ASSERT_NOT_REACHED();
280         return true;
281     };
282
283     logToConsole(frame, url, resourceRequest.requestContext(), allowed);
284     return !allowed;
285 }
286
287 bool MixedContentChecker::canDisplayInsecureContentInternal(SecurityOrigin* securityOrigin, const KURL& url, const MixedContentType type) const
288 {
289     // Check the top frame if it differs from MixedContentChecker's m_frame.
290     if (!m_frame->tree().top()->isLocalFrame()) {
291         // FIXME: We need a way to access the top-level frame's MixedContentChecker when that frame
292         // is in a different process from the current frame. Until that is done, we always allow
293         // loads in remote frames.
294         return false;
295     }
296     Frame* top = m_frame->tree().top();
297     if (top != m_frame && !toLocalFrame(top)->loader().mixedContentChecker()->canDisplayInsecureContent(toLocalFrame(top)->document()->securityOrigin(), url))
298         return false;
299
300     // Just count these for the moment, don't block them.
301     if (Platform::current()->isReservedIPAddress(url) && !Platform::current()->isReservedIPAddress(KURL(ParsedURLString, securityOrigin->toString())))
302         UseCounter::count(m_frame->document(), UseCounter::MixedContentPrivateIPInPublicWebsitePassive);
303
304     // Then check the current frame:
305     if (!isMixedContent(securityOrigin, url))
306         return true;
307
308     Settings* settings = m_frame->settings();
309     bool allowed = client()->allowDisplayingInsecureContent(settings && settings->allowDisplayOfInsecureContent(), securityOrigin, url);
310     logWarning(allowed, url, type);
311
312     if (allowed)
313         client()->didDisplayInsecureContent();
314
315     return allowed;
316 }
317
318 bool MixedContentChecker::canRunInsecureContentInternal(SecurityOrigin* securityOrigin, const KURL& url, const MixedContentType type) const
319 {
320     // Check the top frame if it differs from MixedContentChecker's m_frame.
321     if (!m_frame->tree().top()->isLocalFrame()) {
322         // FIXME: We need a way to access the top-level frame's MixedContentChecker when that frame
323         // is in a different process from the current frame. Until that is done, we always allow
324         // loads in remote frames.
325         return true;
326     }
327     Frame* top = m_frame->tree().top();
328     if (top != m_frame && !toLocalFrame(top)->loader().mixedContentChecker()->canRunInsecureContent(toLocalFrame(top)->document()->securityOrigin(), url))
329         return false;
330
331     // Just count these for the moment, don't block them.
332     if (Platform::current()->isReservedIPAddress(url) && !Platform::current()->isReservedIPAddress(KURL(ParsedURLString, securityOrigin->toString())))
333         UseCounter::count(m_frame->document(), UseCounter::MixedContentPrivateIPInPublicWebsiteActive);
334
335     // Then check the current frame:
336     if (!isMixedContent(securityOrigin, url))
337         return true;
338
339     Settings* settings = m_frame->settings();
340     bool allowedPerSettings = settings && (settings->allowRunningOfInsecureContent() || ((type == WebSocket) && settings->allowConnectingInsecureWebSocket()));
341     bool allowed = client()->allowRunningInsecureContent(allowedPerSettings, securityOrigin, url);
342     logWarning(allowed, url, type);
343
344     if (allowed)
345         client()->didRunInsecureContent(securityOrigin, url);
346
347     return allowed;
348 }
349
350 bool MixedContentChecker::canFrameInsecureContent(SecurityOrigin* securityOrigin, const KURL& url) const
351 {
352     // If we're dealing with a CORS-enabled scheme, then block mixed frames as active content. Otherwise,
353     // treat frames as passive content.
354     //
355     // FIXME: Remove this temporary hack once we have a reasonable API for launching external applications
356     // via URLs. http://crbug.com/318788 and https://crbug.com/393481
357     if (SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(url.protocol()))
358         return canRunInsecureContentInternal(securityOrigin, url, MixedContentChecker::Execution);
359     return canDisplayInsecureContentInternal(securityOrigin, url, MixedContentChecker::Display);
360 }
361
362 bool MixedContentChecker::canConnectInsecureWebSocket(SecurityOrigin* securityOrigin, const KURL& url) const
363 {
364     if (RuntimeEnabledFeatures::laxMixedContentCheckingEnabled())
365         return canDisplayInsecureContentInternal(securityOrigin, url, MixedContentChecker::WebSocket);
366     return canRunInsecureContentInternal(securityOrigin, url, MixedContentChecker::WebSocket);
367 }
368
369 bool MixedContentChecker::canSubmitToInsecureForm(SecurityOrigin* securityOrigin, const KURL& url) const
370 {
371     // For whatever reason, some folks handle forms via JavaScript, and submit to `javascript:void(0)`
372     // rather than calling `preventDefault()`. We special-case `javascript:` URLs here, as they don't
373     // introduce MixedContent for form submissions.
374     if (url.protocolIs("javascript"))
375         return true;
376
377     // If lax mixed content checking is enabled (noooo!), skip this check entirely.
378     if (RuntimeEnabledFeatures::laxMixedContentCheckingEnabled())
379         return true;
380     return canDisplayInsecureContentInternal(securityOrigin, url, MixedContentChecker::Submission);
381 }
382
383 void MixedContentChecker::logWarning(bool allowed, const KURL& target, const MixedContentType type) const
384 {
385     StringBuilder message;
386     message.append((allowed ? "" : "[blocked] "));
387     message.append("The page at '" + m_frame->document()->url().elidedString() + "' was loaded over HTTPS, but ");
388     switch (type) {
389     case Display:
390         message.append("displayed insecure content from '" + target.elidedString() + "': this content should also be loaded over HTTPS.\n");
391         break;
392     case Execution:
393     case WebSocket:
394         message.append("ran insecure content from '" + target.elidedString() + "': this content should also be loaded over HTTPS.\n");
395         break;
396     case Submission:
397         message.append("is submitting data to an insecure location at '" + target.elidedString() + "': this content should also be submitted over HTTPS.\n");
398         break;
399     }
400     MessageLevel messageLevel = allowed ? WarningMessageLevel : ErrorMessageLevel;
401     m_frame->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessageSource, messageLevel, message.toString()));
402 }
403
404 void MixedContentChecker::checkMixedPrivatePublic(LocalFrame* frame, const AtomicString& resourceIPAddress)
405 {
406     if (!frame || !frame->document() || !frame->document()->loader())
407         return;
408
409     KURL documentIP(ParsedURLString, "http://" + frame->document()->loader()->response().remoteIPAddress());
410     KURL resourceIP(ParsedURLString, "http://" + resourceIPAddress);
411
412     // Just count these for the moment, don't block them.
413     //
414     // FIXME: Once we know how we want to check this, adjust the platform APIs to avoid the KURL construction.
415     if (Platform::current()->isReservedIPAddress(resourceIP) && !Platform::current()->isReservedIPAddress(documentIP))
416         UseCounter::count(frame->document(), UseCounter::MixedContentPrivateHostnameInPublicHostname);
417 }
418
419 void MixedContentChecker::trace(Visitor* visitor)
420 {
421     visitor->trace(m_frame);
422 }
423
424 } // namespace blink