blob: 64fb6f464322751fc09f15a89d0f305ccb2ce1fe [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "core/loader/MixedContentChecker.h"
#include "core/dom/Document.h"
#include "core/frame/ContentSettingsClient.h"
#include "core/frame/Frame.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameClient.h"
#include "core/frame/Settings.h"
#include "core/frame/UseCounter.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/loader/DocumentLoader.h"
#include "core/workers/WorkerContentSettingsClient.h"
#include "core/workers/WorkerGlobalScope.h"
#include "core/workers/WorkerOrWorkletGlobalScope.h"
#include "core/workers/WorkerSettings.h"
#include "platform/network/NetworkUtils.h"
#include "platform/weborigin/SchemeRegistry.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/wtf/text/StringBuilder.h"
#include "public/mojom/net/ip_address_space.mojom-blink.h"
#include "public/platform/WebInsecureRequestPolicy.h"
#include "public/platform/WebMixedContent.h"
#include "public/platform/WebSecurityOrigin.h"
#include "public/platform/WebWorkerFetchContext.h"
#include "services/network/public/mojom/request_context_frame_type.mojom-blink.h"
namespace blink {
namespace {
// When a frame is local, use its full URL to represent the main resource. When
// the frame is remote, the full URL isn't accessible, so use the origin. This
// function is used, for example, to determine the URL to show in console
// messages about mixed content.
KURL MainResourceUrlForFrame(Frame* frame) {
if (frame->IsRemoteFrame()) {
return KURL(NullURL(),
frame->GetSecurityContext()->GetSecurityOrigin()->ToString());
}
return ToLocalFrame(frame)->GetDocument()->Url();
}
const char* RequestContextName(WebURLRequest::RequestContext context) {
switch (context) {
case WebURLRequest::kRequestContextAudio:
return "audio file";
case WebURLRequest::kRequestContextBeacon:
return "Beacon endpoint";
case WebURLRequest::kRequestContextCSPReport:
return "Content Security Policy reporting endpoint";
case WebURLRequest::kRequestContextDownload:
return "download";
case WebURLRequest::kRequestContextEmbed:
return "plugin resource";
case WebURLRequest::kRequestContextEventSource:
return "EventSource endpoint";
case WebURLRequest::kRequestContextFavicon:
return "favicon";
case WebURLRequest::kRequestContextFetch:
return "resource";
case WebURLRequest::kRequestContextFont:
return "font";
case WebURLRequest::kRequestContextForm:
return "form action";
case WebURLRequest::kRequestContextFrame:
return "frame";
case WebURLRequest::kRequestContextHyperlink:
return "resource";
case WebURLRequest::kRequestContextIframe:
return "frame";
case WebURLRequest::kRequestContextImage:
return "image";
case WebURLRequest::kRequestContextImageSet:
return "image";
case WebURLRequest::kRequestContextImport:
return "HTML Import";
case WebURLRequest::kRequestContextInternal:
return "resource";
case WebURLRequest::kRequestContextLocation:
return "resource";
case WebURLRequest::kRequestContextManifest:
return "manifest";
case WebURLRequest::kRequestContextObject:
return "plugin resource";
case WebURLRequest::kRequestContextPing:
return "hyperlink auditing endpoint";
case WebURLRequest::kRequestContextPlugin:
return "plugin data";
case WebURLRequest::kRequestContextPrefetch:
return "prefetch resource";
case WebURLRequest::kRequestContextScript:
return "script";
case WebURLRequest::kRequestContextServiceWorker:
return "Service Worker script";
case WebURLRequest::kRequestContextSharedWorker:
return "Shared Worker script";
case WebURLRequest::kRequestContextStyle:
return "stylesheet";
case WebURLRequest::kRequestContextSubresource:
return "resource";
case WebURLRequest::kRequestContextTrack:
return "Text Track";
case WebURLRequest::kRequestContextUnspecified:
return "resource";
case WebURLRequest::kRequestContextVideo:
return "video";
case WebURLRequest::kRequestContextWorker:
return "Worker script";
case WebURLRequest::kRequestContextXMLHttpRequest:
return "XMLHttpRequest endpoint";
case WebURLRequest::kRequestContextXSLT:
return "XSLT";
}
NOTREACHED();
return "resource";
}
// TODO(nhiroki): Consider adding interfaces for Settings/WorkerSettings and
// ContentSettingsClient/WorkerContentSettingsClient to avoid using C++
// template.
template <typename SettingsType, typename SettingsClientType>
bool IsWebSocketAllowedImpl(ExecutionContext* execution_context,
SecurityContext* security_context,
const SecurityOrigin* security_origin,
SettingsType* settings,
SettingsClientType* settings_client,
const KURL& url) {
UseCounter::Count(execution_context, WebFeature::kMixedContentPresent);
UseCounter::Count(execution_context, WebFeature::kMixedContentWebSocket);
if (ContentSecurityPolicy* policy =
security_context->GetContentSecurityPolicy()) {
policy->ReportMixedContent(url,
ResourceRequest::RedirectStatus::kNoRedirect);
}
// If we're in strict mode, we'll automagically fail everything, and
// intentionally skip the client checks in order to prevent degrading the
// site's security UI.
bool strict_mode =
security_context->GetInsecureRequestPolicy() & kBlockAllMixedContent ||
settings->GetStrictMixedContentChecking();
if (strict_mode)
return false;
bool allowed_per_settings =
settings && settings->GetAllowRunningOfInsecureContent();
return settings_client->AllowRunningInsecureContent(allowed_per_settings,
security_origin, url);
}
} // namespace
static void MeasureStricterVersionOfIsMixedContent(Frame& frame,
const KURL& url,
const LocalFrame* source) {
// We're currently only checking for mixed content in `https://*` contexts.
// What about other "secure" contexts the SchemeRegistry knows about? We'll
// use this method to measure the occurrence of non-webby mixed content to
// make sure we're not breaking the world without realizing it.
const SecurityOrigin* origin =
frame.GetSecurityContext()->GetSecurityOrigin();
if (MixedContentChecker::IsMixedContent(origin, url)) {
if (origin->Protocol() != "https") {
UseCounter::Count(
source,
WebFeature::kMixedContentInNonHTTPSFrameThatRestrictsMixedContent);
}
} else if (!SecurityOrigin::IsSecure(url) &&
SchemeRegistry::ShouldTreatURLSchemeAsSecure(origin->Protocol())) {
UseCounter::Count(
source,
WebFeature::kMixedContentInSecureFrameThatDoesNotRestrictMixedContent);
}
}
bool RequestIsSubframeSubresource(
Frame* frame,
network::mojom::RequestContextFrameType frame_type) {
return (frame && frame != frame->Tree().Top() &&
frame_type != network::mojom::RequestContextFrameType::kNested);
}
// static
bool MixedContentChecker::IsMixedContent(const SecurityOrigin* security_origin,
const KURL& url) {
if (!SchemeRegistry::ShouldTreatURLSchemeAsRestrictingMixedContent(
security_origin->Protocol()))
return false;
// |url| is mixed content if its origin is not potentially trustworthy nor
// secure. We do a quick check against `SecurityOrigin::isSecure` to catch
// things like `about:blank`, which cannot be sanely passed into
// `SecurityOrigin::create` (as their origin depends on their context).
// blob: and filesystem: URLs never hit the network, and access is restricted
// to same-origin contexts, so they are not blocked either.
bool is_allowed = url.ProtocolIs("blob") || url.ProtocolIs("filesystem") ||
SecurityOrigin::IsSecure(url) ||
SecurityOrigin::Create(url)->IsPotentiallyTrustworthy();
return !is_allowed;
}
// static
Frame* MixedContentChecker::InWhichFrameIsContentMixed(
Frame* frame,
network::mojom::RequestContextFrameType frame_type,
const KURL& url,
const LocalFrame* source) {
// We only care about subresource loads; top-level navigations cannot be mixed
// content. Neither can frameless requests.
if (frame_type == network::mojom::RequestContextFrameType::kTopLevel ||
!frame)
return nullptr;
// Check the top frame first.
Frame& top = frame->Tree().Top();
MeasureStricterVersionOfIsMixedContent(top, url, source);
if (IsMixedContent(top.GetSecurityContext()->GetSecurityOrigin(), url))
return &top;
MeasureStricterVersionOfIsMixedContent(*frame, url, source);
if (IsMixedContent(frame->GetSecurityContext()->GetSecurityOrigin(), url))
return frame;
// No mixed content, no problem.
return nullptr;
}
// static
void MixedContentChecker::LogToConsoleAboutFetch(
ExecutionContext* execution_context,
const KURL& main_resource_url,
const KURL& url,
WebURLRequest::RequestContext request_context,
bool allowed,
std::unique_ptr<SourceLocation> source_location) {
String message = String::Format(
"Mixed Content: The page at '%s' was loaded over HTTPS, but requested an "
"insecure %s '%s'. %s",
main_resource_url.ElidedString().Utf8().data(),
RequestContextName(request_context), url.ElidedString().Utf8().data(),
allowed ? "This content should also be served over HTTPS."
: "This request has been blocked; the content must be served "
"over HTTPS.");
MessageLevel message_level =
allowed ? kWarningMessageLevel : kErrorMessageLevel;
if (source_location) {
execution_context->AddConsoleMessage(
ConsoleMessage::Create(kSecurityMessageSource, message_level, message,
std::move(source_location)));
} else {
execution_context->AddConsoleMessage(
ConsoleMessage::Create(kSecurityMessageSource, message_level, message));
}
}
// static
void MixedContentChecker::Count(Frame* frame,
WebURLRequest::RequestContext request_context,
const LocalFrame* source) {
UseCounter::Count(source, WebFeature::kMixedContentPresent);
// Roll blockable content up into a single counter, count unblocked types
// individually so we can determine when they can be safely moved to the
// blockable category:
WebMixedContentContextType context_type =
WebMixedContent::ContextTypeFromRequestContext(
request_context,
frame->GetSettings()->GetStrictMixedContentCheckingForPlugin());
if (context_type == WebMixedContentContextType::kBlockable) {
UseCounter::Count(source, WebFeature::kMixedContentBlockable);
return;
}
WebFeature feature;
switch (request_context) {
case WebURLRequest::kRequestContextAudio:
feature = WebFeature::kMixedContentAudio;
break;
case WebURLRequest::kRequestContextDownload:
feature = WebFeature::kMixedContentDownload;
break;
case WebURLRequest::kRequestContextFavicon:
feature = WebFeature::kMixedContentFavicon;
break;
case WebURLRequest::kRequestContextImage:
feature = WebFeature::kMixedContentImage;
break;
case WebURLRequest::kRequestContextInternal:
feature = WebFeature::kMixedContentInternal;
break;
case WebURLRequest::kRequestContextPlugin:
feature = WebFeature::kMixedContentPlugin;
break;
case WebURLRequest::kRequestContextPrefetch:
feature = WebFeature::kMixedContentPrefetch;
break;
case WebURLRequest::kRequestContextVideo:
feature = WebFeature::kMixedContentVideo;
break;
default:
NOTREACHED();
return;
}
UseCounter::Count(source, feature);
}
// static
bool MixedContentChecker::ShouldBlockFetch(
LocalFrame* frame,
WebURLRequest::RequestContext request_context,
network::mojom::RequestContextFrameType frame_type,
ResourceRequest::RedirectStatus redirect_status,
const KURL& url,
SecurityViolationReportingPolicy reporting_policy) {
// Frame-level loads are checked by the browser if PlzNavigate is enabled. No
// need to check them again here.
if (frame->GetSettings()->GetBrowserSideNavigationEnabled() &&
frame_type != network::mojom::RequestContextFrameType::kNone) {
return false;
}
Frame* effective_frame = EffectiveFrameForFrameType(frame, frame_type);
Frame* mixed_frame =
InWhichFrameIsContentMixed(effective_frame, frame_type, url, frame);
if (!mixed_frame)
return false;
MixedContentChecker::Count(mixed_frame, request_context, frame);
if (ContentSecurityPolicy* policy =
frame->GetSecurityContext()->GetContentSecurityPolicy())
policy->ReportMixedContent(url, redirect_status);
Settings* settings = mixed_frame->GetSettings();
// Use the current local frame's client; the embedder doesn't distinguish
// mixed content signals from different frames on the same page.
LocalFrameClient* client = frame->Client();
ContentSettingsClient* content_settings_client =
frame->GetContentSettingsClient();
const SecurityOrigin* security_origin =
mixed_frame->GetSecurityContext()->GetSecurityOrigin();
bool allowed = false;
// If we're in strict mode, we'll automagically fail everything, and
// intentionally skip the client checks in order to prevent degrading the
// site's security UI.
bool strict_mode =
mixed_frame->GetSecurityContext()->GetInsecureRequestPolicy() &
kBlockAllMixedContent ||
settings->GetStrictMixedContentChecking();
WebMixedContentContextType context_type =
WebMixedContent::ContextTypeFromRequestContext(
request_context, settings->GetStrictMixedContentCheckingForPlugin());
// If we're loading the main resource of a subframe, we need to take a close
// look at the loaded URL. If we're dealing with a CORS-enabled scheme, then
// block mixed frames as active content. Otherwise, treat frames as passive
// content.
//
// FIXME: Remove this temporary hack once we have a reasonable API for
// launching external applications via URLs. http://crbug.com/318788 and
// https://crbug.com/393481
if (frame_type == network::mojom::RequestContextFrameType::kNested &&
!SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled(url.Protocol()))
context_type = WebMixedContentContextType::kOptionallyBlockable;
switch (context_type) {
case WebMixedContentContextType::kOptionallyBlockable:
allowed = !strict_mode;
if (allowed) {
content_settings_client->PassiveInsecureContentFound(url);
client->DidDisplayInsecureContent();
}
break;
case WebMixedContentContextType::kBlockable: {
// Strictly block subresources that are mixed with respect to their
// subframes, unless all insecure content is allowed. This is to avoid the
// following situation: https://a.com embeds https://b.com, which loads a
// script over insecure HTTP. The user opts to allow the insecure content,
// thinking that they are allowing an insecure script to run on
// https://a.com and not realizing that they are in fact allowing an
// insecure script on https://b.com.
if (!settings->GetAllowRunningOfInsecureContent() &&
RequestIsSubframeSubresource(effective_frame, frame_type) &&
IsMixedContent(frame->GetSecurityContext()->GetSecurityOrigin(),
url)) {
UseCounter::Count(frame,
WebFeature::kBlockableMixedContentInSubframeBlocked);
allowed = false;
break;
}
bool should_ask_embedder =
!strict_mode && settings &&
(!settings->GetStrictlyBlockBlockableMixedContent() ||
settings->GetAllowRunningOfInsecureContent());
allowed = should_ask_embedder &&
content_settings_client->AllowRunningInsecureContent(
settings && settings->GetAllowRunningOfInsecureContent(),
security_origin, url);
if (allowed) {
client->DidRunInsecureContent(security_origin, url);
UseCounter::Count(frame, WebFeature::kMixedContentBlockableAllowed);
}
break;
}
case WebMixedContentContextType::kShouldBeBlockable:
allowed = !strict_mode;
if (allowed)
client->DidDisplayInsecureContent();
break;
case WebMixedContentContextType::kNotMixedContent:
NOTREACHED();
break;
};
if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
LogToConsoleAboutFetch(frame->GetDocument(),
MainResourceUrlForFrame(mixed_frame), url,
request_context, allowed, nullptr);
}
return !allowed;
}
// static
bool MixedContentChecker::ShouldBlockFetchOnWorker(
WorkerOrWorkletGlobalScope* global_scope,
WebWorkerFetchContext* worker_fetch_context,
WebURLRequest::RequestContext request_context,
network::mojom::RequestContextFrameType frame_type,
ResourceRequest::RedirectStatus redirect_status,
const KURL& url,
SecurityViolationReportingPolicy reporting_policy) {
if (!MixedContentChecker::IsMixedContent(global_scope->GetSecurityOrigin(),
url)) {
return false;
}
UseCounter::Count(global_scope, WebFeature::kMixedContentPresent);
UseCounter::Count(global_scope, WebFeature::kMixedContentBlockable);
if (ContentSecurityPolicy* policy = global_scope->GetContentSecurityPolicy())
policy->ReportMixedContent(url, redirect_status);
// Blocks all mixed content request from worklets.
// TODO(horo): Revise this when the spec is updated.
// Worklets spec: https://www.w3.org/TR/worklets-1/#security-considerations
// Spec issue: https://github.com/w3c/css-houdini-drafts/issues/92
if (!global_scope->IsWorkerGlobalScope())
return true;
WorkerGlobalScope* worker_global_scope = ToWorkerGlobalScope(global_scope);
WorkerSettings* settings = worker_global_scope->GetWorkerSettings();
DCHECK(settings);
bool allowed = false;
if (!settings->GetAllowRunningOfInsecureContent() &&
worker_fetch_context->IsOnSubframe()) {
UseCounter::Count(global_scope,
WebFeature::kBlockableMixedContentInSubframeBlocked);
allowed = false;
} else {
bool strict_mode = worker_global_scope->GetInsecureRequestPolicy() &
kBlockAllMixedContent ||
settings->GetStrictMixedContentChecking();
bool should_ask_embedder =
!strict_mode && (!settings->GetStrictlyBlockBlockableMixedContent() ||
settings->GetAllowRunningOfInsecureContent());
allowed = should_ask_embedder &&
WorkerContentSettingsClient::From(*global_scope)
->AllowRunningInsecureContent(
settings->GetAllowRunningOfInsecureContent(),
global_scope->GetSecurityOrigin(), url);
if (allowed) {
worker_fetch_context->DidRunInsecureContent(
WebSecurityOrigin(global_scope->GetSecurityOrigin()), url);
UseCounter::Count(global_scope,
WebFeature::kMixedContentBlockableAllowed);
}
}
if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
LogToConsoleAboutFetch(global_scope, global_scope->Url(), url,
request_context, allowed, nullptr);
}
return !allowed;
}
// static
void MixedContentChecker::LogToConsoleAboutWebSocket(
ExecutionContext* execution_context,
const KURL& main_resource_url,
const KURL& url,
bool allowed) {
String message = String::Format(
"Mixed Content: The page at '%s' was loaded over HTTPS, but attempted to "
"connect to the insecure WebSocket endpoint '%s'. %s",
main_resource_url.ElidedString().Utf8().data(),
url.ElidedString().Utf8().data(),
allowed ? "This endpoint should be available via WSS. Insecure access is "
"deprecated."
: "This request has been blocked; this endpoint must be "
"available over WSS.");
MessageLevel message_level =
allowed ? kWarningMessageLevel : kErrorMessageLevel;
execution_context->AddConsoleMessage(
ConsoleMessage::Create(kSecurityMessageSource, message_level, message));
}
// static
bool MixedContentChecker::IsWebSocketAllowed(LocalFrame* frame,
const KURL& url) {
Frame* mixed_frame = InWhichFrameIsContentMixed(
frame, network::mojom::RequestContextFrameType::kNone, url, frame);
if (!mixed_frame)
return true;
Settings* settings = mixed_frame->GetSettings();
// Use the current local frame's client; the embedder doesn't distinguish
// mixed content signals from different frames on the same page.
ContentSettingsClient* content_settings_client =
frame->GetContentSettingsClient();
SecurityContext* security_context = mixed_frame->GetSecurityContext();
const SecurityOrigin* security_origin = security_context->GetSecurityOrigin();
bool allowed = IsWebSocketAllowedImpl(frame->GetDocument(), security_context,
security_origin, settings,
content_settings_client, url);
if (allowed)
frame->Client()->DidRunInsecureContent(security_origin, url);
LogToConsoleAboutWebSocket(
frame->GetDocument(), MainResourceUrlForFrame(mixed_frame), url, allowed);
return allowed;
}
// static
bool MixedContentChecker::IsWebSocketAllowed(
WorkerGlobalScope* global_scope,
WebWorkerFetchContext* worker_fetch_context,
const KURL& url) {
if (!MixedContentChecker::IsMixedContent(global_scope->GetSecurityOrigin(),
url)) {
return true;
}
WorkerSettings* settings = global_scope->GetWorkerSettings();
WorkerContentSettingsClient* content_settings_client =
WorkerContentSettingsClient::From(*global_scope);
SecurityContext* security_context = &global_scope->GetSecurityContext();
const SecurityOrigin* security_origin = global_scope->GetSecurityOrigin();
bool allowed =
IsWebSocketAllowedImpl(global_scope, security_context, security_origin,
settings, content_settings_client, url);
if (allowed) {
worker_fetch_context->DidRunInsecureContent(
WebSecurityOrigin(security_origin), url);
}
LogToConsoleAboutWebSocket(global_scope, global_scope->Url(), url, allowed);
return allowed;
}
bool MixedContentChecker::IsMixedFormAction(
LocalFrame* frame,
const KURL& url,
SecurityViolationReportingPolicy reporting_policy) {
// For whatever reason, some folks handle forms via JavaScript, and submit to
// `javascript:void(0)` rather than calling `preventDefault()`. We
// special-case `javascript:` URLs here, as they don't introduce MixedContent
// for form submissions.
if (url.ProtocolIs("javascript"))
return false;
Frame* mixed_frame = InWhichFrameIsContentMixed(
frame, network::mojom::RequestContextFrameType::kNone, url, frame);
if (!mixed_frame)
return false;
UseCounter::Count(frame, WebFeature::kMixedContentPresent);
// Use the current local frame's client; the embedder doesn't distinguish
// mixed content signals from different frames on the same page.
frame->Client()->DidContainInsecureFormAction();
if (reporting_policy == SecurityViolationReportingPolicy::kReport) {
String message = String::Format(
"Mixed Content: The page at '%s' was loaded over a secure connection, "
"but contains a form that targets an insecure endpoint '%s'. This "
"endpoint should be made available over a secure connection.",
MainResourceUrlForFrame(mixed_frame).ElidedString().Utf8().data(),
url.ElidedString().Utf8().data());
frame->GetDocument()->AddConsoleMessage(ConsoleMessage::Create(
kSecurityMessageSource, kWarningMessageLevel, message));
}
return true;
}
void MixedContentChecker::CheckMixedPrivatePublic(
LocalFrame* frame,
const AtomicString& resource_ip_address) {
if (!frame || !frame->GetDocument() || !frame->GetDocument()->Loader())
return;
// Just count these for the moment, don't block them.
if (NetworkUtils::IsReservedIPAddress(resource_ip_address) &&
frame->GetDocument()->AddressSpace() == mojom::IPAddressSpace::kPublic) {
UseCounter::Count(frame->GetDocument(),
WebFeature::kMixedContentPrivateHostnameInPublicHostname);
// We can simplify the IP checks here, as we've already verified that
// |resourceIPAddress| is a reserved IP address, which means it's also a
// valid IP address in a normalized form.
if (resource_ip_address.StartsWith("127.0.0.") ||
resource_ip_address == "[::1]") {
UseCounter::Count(frame->GetDocument(),
frame->GetDocument()->IsSecureContext()
? WebFeature::kLoopbackEmbeddedInSecureContext
: WebFeature::kLoopbackEmbeddedInNonSecureContext);
}
}
}
Frame* MixedContentChecker::EffectiveFrameForFrameType(
LocalFrame* frame,
network::mojom::RequestContextFrameType frame_type) {
// If we're loading the main resource of a subframe, ensure that we check
// against the parent of the active frame, rather than the frame itself.
if (frame_type != network::mojom::RequestContextFrameType::kNested)
return frame;
Frame* parent_frame = frame->Tree().Parent();
DCHECK(parent_frame);
return parent_frame;
}
void MixedContentChecker::HandleCertificateError(
LocalFrame* frame,
const ResourceResponse& response,
network::mojom::RequestContextFrameType frame_type,
WebURLRequest::RequestContext request_context) {
Frame* effective_frame = EffectiveFrameForFrameType(frame, frame_type);
if (frame_type == network::mojom::RequestContextFrameType::kTopLevel ||
!effective_frame)
return;
// Use the current local frame's client; the embedder doesn't distinguish
// mixed content signals from different frames on the same page.
LocalFrameClient* client = frame->Client();
bool strict_mixed_content_checking_for_plugin =
effective_frame->GetSettings() &&
effective_frame->GetSettings()->GetStrictMixedContentCheckingForPlugin();
WebMixedContentContextType context_type =
WebMixedContent::ContextTypeFromRequestContext(
request_context, strict_mixed_content_checking_for_plugin);
if (context_type == WebMixedContentContextType::kBlockable) {
client->DidRunContentWithCertificateErrors();
} else {
// contextTypeFromRequestContext() never returns NotMixedContent (it
// computes the type of mixed content, given that the content is mixed).
DCHECK_NE(context_type, WebMixedContentContextType::kNotMixedContent);
client->DidDisplayContentWithCertificateErrors();
}
}
// static
void MixedContentChecker::MixedContentFound(
LocalFrame* frame,
const KURL& main_resource_url,
const KURL& mixed_content_url,
WebURLRequest::RequestContext request_context,
bool was_allowed,
bool had_redirect,
std::unique_ptr<SourceLocation> source_location) {
// Logs to the frame console.
LogToConsoleAboutFetch(frame->GetDocument(), main_resource_url,
mixed_content_url, request_context, was_allowed,
std::move(source_location));
// Reports to the CSP policy.
ContentSecurityPolicy* policy =
frame->GetSecurityContext()->GetContentSecurityPolicy();
if (policy) {
policy->ReportMixedContent(
mixed_content_url,
had_redirect ? ResourceRequest::RedirectStatus::kFollowedRedirect
: ResourceRequest::RedirectStatus::kNoRedirect);
}
}
WebMixedContentContextType MixedContentChecker::ContextTypeForInspector(
LocalFrame* frame,
const ResourceRequest& request) {
Frame* effective_frame =
EffectiveFrameForFrameType(frame, request.GetFrameType());
Frame* mixed_frame = InWhichFrameIsContentMixed(
effective_frame, request.GetFrameType(), request.Url(), frame);
if (!mixed_frame)
return WebMixedContentContextType::kNotMixedContent;
// See comment in ShouldBlockFetch() about loading the main resource of a
// subframe.
if (request.GetFrameType() ==
network::mojom::RequestContextFrameType::kNested &&
!SchemeRegistry::ShouldTreatURLSchemeAsCORSEnabled(
request.Url().Protocol())) {
return WebMixedContentContextType::kOptionallyBlockable;
}
bool strict_mixed_content_checking_for_plugin =
mixed_frame->GetSettings() &&
mixed_frame->GetSettings()->GetStrictMixedContentCheckingForPlugin();
return WebMixedContent::ContextTypeFromRequestContext(
request.GetRequestContext(), strict_mixed_content_checking_for_plugin);
}
} // namespace blink