nsMixedContentBlocker.cpp (40203B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "nsMixedContentBlocker.h" 8 9 #include "mozilla/BasePrincipal.h" 10 #include "mozilla/LoadInfo.h" 11 #include "mozilla/Logging.h" 12 #include "mozilla/Preferences.h" 13 #include "mozilla/StaticPrefs_dom.h" 14 #include "mozilla/StaticPrefs_fission.h" 15 #include "mozilla/StaticPrefs_security.h" 16 #include "mozilla/dom/BrowsingContext.h" 17 #include "mozilla/dom/ContentChild.h" 18 #include "mozilla/dom/Document.h" 19 #include "mozilla/dom/WindowContext.h" 20 #include "mozilla/dom/nsHTTPSOnlyUtils.h" 21 #include "mozilla/glean/DomSecurityMetrics.h" 22 #include "mozilla/ipc/URIUtils.h" 23 #include "mozilla/net/DNS.h" 24 #include "mozilla/net/DocumentChannel.h" 25 #include "mozilla/net/DocumentLoadListener.h" 26 #include "nsAsyncRedirectVerifyHelper.h" 27 #include "nsCOMPtr.h" 28 #include "nsCSPContext.h" 29 #include "nsCharSeparatedTokenizer.h" 30 #include "nsContentPolicyUtils.h" 31 #include "nsContentUtils.h" 32 #include "nsDocShell.h" 33 #include "nsIChannel.h" 34 #include "nsIChannelEventSink.h" 35 #include "nsINode.h" 36 #include "nsIParentChannel.h" 37 #include "nsIProtocolHandler.h" 38 #include "nsIScriptError.h" 39 #include "nsIScriptObjectPrincipal.h" 40 #include "nsISecureBrowserUI.h" 41 #include "nsISiteSecurityService.h" 42 #include "nsIURI.h" 43 #include "nsIWebNavigation.h" 44 #include "nsIWebProgressListener.h" 45 #include "nsLoadGroup.h" 46 #include "nsNetUtil.h" 47 #include "nsQueryObject.h" 48 #include "nsThreadUtils.h" 49 #include "prnetdb.h" 50 51 using namespace mozilla; 52 using namespace mozilla::dom; 53 54 static mozilla::LazyLogModule sMCBLog("MCBLog"); 55 56 enum nsMixedContentBlockerMessageType { eBlocked = 0x00, eUserOverride = 0x01 }; 57 58 // Allowlist of hostnames that should be considered secure contexts even when 59 // served over http:// or ws:// 60 nsCString* nsMixedContentBlocker::sSecurecontextAllowlist = nullptr; 61 bool nsMixedContentBlocker::sSecurecontextAllowlistCached = false; 62 63 enum MixedContentHSTSState { 64 MCB_HSTS_PASSIVE_NO_HSTS = 0, 65 MCB_HSTS_PASSIVE_WITH_HSTS = 1, 66 MCB_HSTS_ACTIVE_NO_HSTS = 2, 67 MCB_HSTS_ACTIVE_WITH_HSTS = 3 68 }; 69 70 nsMixedContentBlocker::~nsMixedContentBlocker() = default; 71 72 NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy, nsIChannelEventSink) 73 74 static void LogMixedContentMessage( 75 MixedContentTypes aClassification, nsIURI* aContentLocation, 76 uint64_t aInnerWindowID, nsMixedContentBlockerMessageType aMessageType, 77 nsIURI* aRequestingLocation, 78 const nsACString& aOverruleMessageLookUpKeyWithThis = ""_ns) { 79 nsAutoCString messageCategory; 80 uint32_t severityFlag; 81 nsAutoCString messageLookupKey; 82 83 if (aMessageType == eBlocked) { 84 severityFlag = nsIScriptError::errorFlag; 85 messageCategory.AssignLiteral("Mixed Content Blocker"); 86 if (aClassification == eMixedDisplay) { 87 messageLookupKey.AssignLiteral("BlockMixedDisplayContent"); 88 } else { 89 messageLookupKey.AssignLiteral("BlockMixedActiveContent"); 90 } 91 } else { 92 severityFlag = nsIScriptError::warningFlag; 93 messageCategory.AssignLiteral("Mixed Content Message"); 94 if (aClassification == eMixedDisplay) { 95 messageLookupKey.AssignLiteral("LoadingMixedDisplayContent2"); 96 } else { 97 messageLookupKey.AssignLiteral("LoadingMixedActiveContent2"); 98 } 99 } 100 101 // if the callee explicitly wants to use a special message for this 102 // console report, then we allow to overrule the default with the 103 // explicitly provided one here. 104 if (!aOverruleMessageLookUpKeyWithThis.IsEmpty()) { 105 messageLookupKey = aOverruleMessageLookUpKeyWithThis; 106 } 107 108 nsAutoString localizedMsg; 109 AutoTArray<nsString, 1> params; 110 CopyUTF8toUTF16(aContentLocation->GetSpecOrDefault(), 111 *params.AppendElement()); 112 nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES, 113 messageLookupKey.get(), params, 114 localizedMsg); 115 116 nsContentUtils::ReportToConsoleByWindowID( 117 localizedMsg, severityFlag, messageCategory, aInnerWindowID, 118 SourceLocation(aRequestingLocation)); 119 } 120 121 /* nsIChannelEventSink implementation 122 * This code is called when a request is redirected. 123 * We check the channel associated with the new uri is allowed to load 124 * in the current context 125 */ 126 NS_IMETHODIMP 127 nsMixedContentBlocker::AsyncOnChannelRedirect( 128 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags, 129 nsIAsyncVerifyRedirectCallback* aCallback) { 130 mozilla::net::nsAsyncRedirectAutoCallback autoCallback(aCallback); 131 132 if (!aOldChannel) { 133 NS_ERROR("No channel when evaluating mixed content!"); 134 return NS_ERROR_FAILURE; 135 } 136 137 // If we are in the parent process in e10s, we don't have access to the 138 // document node, and hence ShouldLoad will fail when we try to get 139 // the docShell. If that's the case, ignore mixed content checks 140 // on redirects in the parent. Let the child check for mixed content. 141 nsCOMPtr<nsIParentChannel> is_ipc_channel; 142 NS_QueryNotificationCallbacks(aNewChannel, is_ipc_channel); 143 RefPtr<net::DocumentLoadListener> docListener = 144 do_QueryObject(is_ipc_channel); 145 if (is_ipc_channel && !docListener) { 146 return NS_OK; 147 } 148 149 // Don't do these checks if we're switching from DocumentChannel 150 // to a real channel. In that case, we should already have done 151 // the checks in the parent process. AsyncOnChannelRedirect 152 // isn't called in the content process if we switch process, 153 // so checking here would just hide bugs in the process switch 154 // cases. 155 if (RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aOldChannel)) { 156 return NS_OK; 157 } 158 159 nsresult rv; 160 nsCOMPtr<nsIURI> oldUri; 161 rv = aOldChannel->GetURI(getter_AddRefs(oldUri)); 162 NS_ENSURE_SUCCESS(rv, rv); 163 164 nsCOMPtr<nsIURI> newUri; 165 rv = aNewChannel->GetURI(getter_AddRefs(newUri)); 166 NS_ENSURE_SUCCESS(rv, rv); 167 168 // Get the loading Info from the old channel 169 nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->LoadInfo(); 170 nsCOMPtr<nsIPrincipal> requestingPrincipal = loadInfo->GetLoadingPrincipal(); 171 172 // Since we are calling shouldLoad() directly on redirects, we don't go 173 // through the code in nsContentPolicyUtils::NS_CheckContentLoadPolicy(). 174 // Hence, we have to duplicate parts of it here. 175 if (requestingPrincipal) { 176 // We check to see if the loadingPrincipal is systemPrincipal and return 177 // early if it is 178 if (requestingPrincipal->IsSystemPrincipal()) { 179 return NS_OK; 180 } 181 } 182 183 int16_t decision = REJECT_REQUEST; 184 rv = ShouldLoad(newUri, loadInfo, &decision); 185 if (NS_FAILED(rv)) { 186 autoCallback.DontCallback(); 187 aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI); 188 return NS_BINDING_FAILED; 189 } 190 191 // If the channel is about to load mixed content, abort the channel 192 if (!NS_CP_ACCEPTED(decision)) { 193 autoCallback.DontCallback(); 194 aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI); 195 return NS_BINDING_FAILED; 196 } 197 198 return NS_OK; 199 } 200 201 /* This version of ShouldLoad() is non-static and called by the Content Policy 202 * API and AsyncOnChannelRedirect(). See nsIContentPolicy::ShouldLoad() 203 * for detailed description of the parameters. 204 */ 205 NS_IMETHODIMP 206 nsMixedContentBlocker::ShouldLoad(nsIURI* aContentLocation, 207 nsILoadInfo* aLoadInfo, int16_t* aDecision) { 208 // We pass in false as the first parameter to ShouldLoad(), because the 209 // callers of this method don't know whether the load went through cached 210 // image redirects. This is handled by direct callers of the static 211 // ShouldLoad. 212 nsresult rv = ShouldLoad(false, // aHadInsecureImageRedirect 213 aContentLocation, aLoadInfo, true, aDecision); 214 215 if (*aDecision == nsIContentPolicy::REJECT_REQUEST) { 216 NS_SetRequestBlockingReason(aLoadInfo, 217 nsILoadInfo::BLOCKING_REASON_MIXED_BLOCKED); 218 } 219 220 return rv; 221 } 222 223 bool nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost( 224 const nsACString& aAsciiHost) { 225 if (mozilla::net::IsLoopbackHostname(aAsciiHost)) { 226 return true; 227 } 228 229 using namespace mozilla::net; 230 NetAddr addr; 231 if (NS_FAILED(addr.InitFromString(aAsciiHost))) { 232 return false; 233 } 234 235 // Step 4 of 236 // https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy says 237 // we should only consider [::1]/128 as a potentially trustworthy IPv6 238 // address, whereas for IPv4 127.0.0.1/8 are considered as potentially 239 // trustworthy. 240 return addr.IsLoopBackAddressWithoutIPv6Mapping(); 241 } 242 243 bool nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(nsIURI* aURL) { 244 if (!aURL) { 245 return false; 246 } 247 nsAutoCString asciiHost; 248 nsresult rv = aURL->GetAsciiHost(asciiHost); 249 NS_ENSURE_SUCCESS(rv, false); 250 return IsPotentiallyTrustworthyLoopbackHost(asciiHost); 251 } 252 253 /* Maybe we have a .onion URL. Treat it as trustworthy as well if 254 * `dom.securecontext.allowlist_onions` is `true`. 255 */ 256 bool nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(nsIURI* aURL) { 257 if (!StaticPrefs::dom_securecontext_allowlist_onions()) { 258 return false; 259 } 260 261 nsAutoCString host; 262 nsresult rv = aURL->GetHost(host); 263 NS_ENSURE_SUCCESS(rv, false); 264 return StringEndsWith(host, ".onion"_ns); 265 } 266 267 // static 268 void nsMixedContentBlocker::OnPrefChange(const char* aPref, void* aClosure) { 269 MOZ_ASSERT(NS_IsMainThread()); 270 MOZ_ASSERT(!strcmp(aPref, "dom.securecontext.allowlist")); 271 Preferences::GetCString("dom.securecontext.allowlist", 272 *sSecurecontextAllowlist); 273 } 274 275 // static 276 void nsMixedContentBlocker::GetSecureContextAllowList(nsACString& aList) { 277 MOZ_ASSERT(NS_IsMainThread()); 278 if (!sSecurecontextAllowlistCached) { 279 MOZ_ASSERT(!sSecurecontextAllowlist); 280 sSecurecontextAllowlistCached = true; 281 sSecurecontextAllowlist = new nsCString(); 282 Preferences::RegisterCallbackAndCall(OnPrefChange, 283 "dom.securecontext.allowlist"); 284 } 285 aList = *sSecurecontextAllowlist; 286 } 287 288 // static 289 void nsMixedContentBlocker::Shutdown() { 290 if (sSecurecontextAllowlist) { 291 delete sSecurecontextAllowlist; 292 sSecurecontextAllowlist = nullptr; 293 } 294 } 295 296 bool nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(nsIURI* aURI) { 297 // The following implements: 298 // https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy 299 300 nsAutoCString scheme; 301 nsresult rv = aURI->GetScheme(scheme); 302 if (NS_FAILED(rv)) { 303 return false; 304 } 305 306 // Blobs are expected to inherit their principal so we don't expect to have 307 // a content principal with scheme 'blob' here. We can't assert that though 308 // since someone could mess with a non-blob URI to give it that scheme. 309 NS_WARNING_ASSERTION(!scheme.EqualsLiteral("blob"), 310 "IsOriginPotentiallyTrustworthy ignoring blob scheme"); 311 312 // According to the specification, the user agent may choose to extend the 313 // trust to other, vendor-specific URL schemes. We use this for "resource:", 314 // which is technically a substituting protocol handler that is not limited to 315 // local resource mapping, but in practice is never mapped remotely as this 316 // would violate assumptions a lot of code makes. 317 // We use nsIProtocolHandler flags to determine which protocols we consider a 318 // priori authenticated. 319 bool aPrioriAuthenticated = false; 320 if (NS_FAILED(NS_URIChainHasFlags( 321 aURI, nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY, 322 &aPrioriAuthenticated))) { 323 return false; 324 } 325 326 if (aPrioriAuthenticated) { 327 return true; 328 } 329 330 nsAutoCString host; 331 rv = aURI->GetHost(host); 332 if (NS_FAILED(rv)) { 333 return false; 334 } 335 336 if (IsPotentiallyTrustworthyLoopbackURL(aURI)) { 337 return true; 338 } 339 340 // If a host is not considered secure according to the default algorithm, then 341 // check to see if it has been allowlisted by the user. We only apply this 342 // allowlist for network resources, i.e., those with scheme "http" or "ws". 343 // The pref should contain a comma-separated list of hostnames. 344 345 if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("ws")) { 346 return false; 347 } 348 349 nsAutoCString allowlist; 350 GetSecureContextAllowList(allowlist); 351 for (const nsACString& allowedHost : 352 nsCCharSeparatedTokenizer(allowlist, ',').ToRange()) { 353 if (host.Equals(allowedHost)) { 354 return true; 355 } 356 } 357 358 // Maybe we have a .onion URL. Treat it as trustworthy as well if 359 // `dom.securecontext.allowlist_onions` is `true`. 360 if (nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(aURI)) { 361 return true; 362 } 363 return false; 364 } 365 366 /* static */ 367 bool nsMixedContentBlocker::IsUpgradableContentType(nsContentPolicyType aType) { 368 MOZ_ASSERT(NS_IsMainThread()); 369 370 switch (aType) { 371 case nsIContentPolicy::TYPE_INTERNAL_IMAGE: 372 case nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD: 373 case nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON: 374 case nsIContentPolicy::TYPE_INTERNAL_AUDIO: 375 case nsIContentPolicy::TYPE_INTERNAL_VIDEO: 376 return true; 377 default: 378 return false; 379 } 380 } 381 382 /* 383 * Return the URI of the precusor principal or the URI of aPrincipal if there is 384 * no precursor URI. 385 */ 386 static already_AddRefed<nsIURI> GetPrincipalURIOrPrecursorPrincipalURI( 387 nsIPrincipal* aPrincipal) { 388 nsCOMPtr<nsIPrincipal> precursorPrincipal = 389 aPrincipal->GetPrecursorPrincipal(); 390 391 #ifdef DEBUG 392 if (precursorPrincipal) { 393 MOZ_ASSERT(aPrincipal->GetIsNullPrincipal(), 394 "Only Null Principals should have a Precursor Principal"); 395 } 396 #endif 397 398 return precursorPrincipal ? precursorPrincipal->GetURI() 399 : aPrincipal->GetURI(); 400 } 401 402 /* Static version of ShouldLoad() that contains all the Mixed Content Blocker 403 * logic. Called from non-static ShouldLoad(). 404 */ 405 nsresult nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, 406 nsIURI* aContentLocation, 407 nsILoadInfo* aLoadInfo, 408 bool aReportError, 409 int16_t* aDecision) { 410 // Asserting that we are on the main thread here and hence do not have to lock 411 // and unlock security.mixed_content.block_active_content and 412 // security.mixed_content.block_display_content before reading/writing to 413 // them. 414 MOZ_ASSERT(NS_IsMainThread()); 415 416 if (MOZ_UNLIKELY(MOZ_LOG_TEST(sMCBLog, LogLevel::Verbose))) { 417 nsAutoCString asciiUrl; 418 aContentLocation->GetAsciiSpec(asciiUrl); 419 MOZ_LOG(sMCBLog, LogLevel::Verbose, ("shouldLoad:")); 420 MOZ_LOG(sMCBLog, LogLevel::Verbose, 421 (" - contentLocation: %s", asciiUrl.get())); 422 } 423 424 nsContentPolicyType internalContentType = 425 aLoadInfo->InternalContentPolicyType(); 426 nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadInfo->GetLoadingPrincipal(); 427 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal(); 428 429 if (MOZ_UNLIKELY(MOZ_LOG_TEST(sMCBLog, LogLevel::Verbose))) { 430 MOZ_LOG(sMCBLog, LogLevel::Verbose, 431 (" - internalContentPolicyType: %s", 432 NS_CP_ContentTypeName(internalContentType))); 433 434 if (loadingPrincipal != nullptr) { 435 nsAutoCString loadingPrincipalAsciiUrl; 436 loadingPrincipal->GetAsciiSpec(loadingPrincipalAsciiUrl); 437 MOZ_LOG(sMCBLog, LogLevel::Verbose, 438 (" - loadingPrincipal: %s", loadingPrincipalAsciiUrl.get())); 439 } else { 440 MOZ_LOG(sMCBLog, LogLevel::Verbose, (" - loadingPrincipal: (nullptr)")); 441 } 442 443 nsAutoCString triggeringPrincipalAsciiUrl; 444 triggeringPrincipal->GetAsciiSpec(triggeringPrincipalAsciiUrl); 445 MOZ_LOG(sMCBLog, LogLevel::Verbose, 446 (" - triggeringPrincipal: %s", triggeringPrincipalAsciiUrl.get())); 447 } 448 449 RefPtr<WindowContext> requestingWindow = 450 WindowContext::GetById(aLoadInfo->GetInnerWindowID()); 451 452 bool isPreload = nsContentUtils::IsPreloadType(internalContentType); 453 454 // The content policy type that we receive may be an internal type for 455 // scripts. Let's remember if we have seen a worker type, and reset it to the 456 // external type in all cases right now. 457 bool isWorkerType = 458 internalContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER || 459 internalContentType == 460 nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE || 461 internalContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER || 462 internalContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER; 463 ExtContentPolicyType contentType = 464 nsContentUtils::InternalContentPolicyTypeToExternal(internalContentType); 465 466 // Assume active (high risk) content and blocked by default 467 MixedContentTypes classification = eMixedScript; 468 // Make decision to block/reject by default 469 *aDecision = REJECT_REQUEST; 470 471 // Notes on non-obvious decisions: 472 // 473 // TYPE_DTD: A DTD can contain entity definitions that expand to scripts. 474 // 475 // TYPE_FONT: The TrueType hinting mechanism is basically a scripting 476 // language that gets interpreted by the operating system's font rasterizer. 477 // Mixed content web fonts are relatively uncommon, and we can can fall back 478 // to built-in fonts with minimal disruption in almost all cases. 479 // 480 // TYPE_CSP_REPORT: High-risk because they directly leak information about 481 // the content of the page, and because blocking them does not have any 482 // negative effect on the page loading. 483 // 484 // TYPE_PING: Ping requests are POSTS, not GETs like images and media. 485 // Also, PING requests have no bearing on the rendering or operation of 486 // the page when used as designed, so even though they are lower risk than 487 // scripts, blocking them is basically risk-free as far as compatibility is 488 // concerned. 489 // 490 // TYPE_STYLESHEET: XSLT stylesheets can insert scripts. CSS positioning 491 // and other advanced CSS features can possibly be exploited to cause 492 // spoofing attacks (e.g. make a "grant permission" button look like a 493 // "refuse permission" button). 494 // 495 // TYPE_BEACON: Beacon requests are similar to TYPE_PING, and are blocked by 496 // default. 497 // 498 // TYPE_WEBSOCKET: The Websockets API requires browsers to 499 // reject mixed-content websockets: "If secure is false but the origin of 500 // the entry script has a scheme component that is itself a secure protocol, 501 // e.g. HTTPS, then throw a SecurityError exception." We already block mixed 502 // content websockets within the websockets implementation, so we don't need 503 // to do any blocking here, nor do we need to provide a way to undo or 504 // override the blocking. Websockets without TLS are very flaky anyway in the 505 // face of many HTTP-aware proxies. Compared to passive content, there is 506 // additional risk that the script using WebSockets will disclose sensitive 507 // information from the HTTPS page and/or eval (directly or indirectly) 508 // received data. 509 // 510 // TYPE_XMLHTTPREQUEST: XHR requires either same origin or CORS, so most 511 // mixed-content XHR will already be blocked by that check. This will also 512 // block HTTPS-to-HTTP XHR with CORS. The same security concerns mentioned 513 // above for WebSockets apply to XHR, and XHR should have the same security 514 // properties as WebSockets w.r.t. mixed content. XHR's handling of redirects 515 // amplifies these concerns. 516 // 517 // TYPE_PROXIED_WEBRTC_MEDIA: Ordinarily, webrtc uses low-level sockets for 518 // peer-to-peer media, which bypasses this code entirely. However, when a 519 // web proxy is being used, the TCP and TLS webrtc connections are routed 520 // through the web proxy (using HTTP CONNECT), which causes these connections 521 // to be checked. We just skip mixed content blocking in that case. 522 523 switch (contentType) { 524 // The top-level document cannot be mixed content by definition 525 case ExtContentPolicy::TYPE_DOCUMENT: 526 *aDecision = ACCEPT; 527 return NS_OK; 528 // Creating insecure websocket connections in a secure page is blocked 529 // already in the websocket constructor. We don't need to check the blocking 530 // here and we don't want to un-block 531 case ExtContentPolicy::TYPE_WEBSOCKET: 532 *aDecision = ACCEPT; 533 return NS_OK; 534 535 // TYPE_SAVEAS_DOWNLOAD: Save-link-as feature is used to download a 536 // resource 537 // without involving a docShell. This kind of loading must be 538 // allowed, if not disabled in the preferences. 539 // Creating insecure connections for a save-as link download is 540 // acceptable. This download is completely disconnected from the docShell, 541 // but still using the same loading principal. 542 543 case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD: 544 *aDecision = ACCEPT; 545 return NS_OK; 546 break; 547 548 // It does not make sense to subject webrtc media connections to mixed 549 // content blocking, since those connections are peer-to-peer and will 550 // therefore almost never match the origin. 551 case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA: 552 *aDecision = ACCEPT; 553 return NS_OK; 554 555 // Static display content is considered moderate risk for mixed content so 556 // these will be blocked according to the mixed display preference 557 case ExtContentPolicy::TYPE_IMAGE: 558 case ExtContentPolicy::TYPE_MEDIA: 559 classification = eMixedDisplay; 560 break; 561 562 // Active content (or content with a low value/risk-of-blocking ratio) 563 // that has been explicitly evaluated; listed here for documentation 564 // purposes and to avoid the assertion and warning for the default case. 565 case ExtContentPolicy::TYPE_BEACON: 566 case ExtContentPolicy::TYPE_CSP_REPORT: 567 case ExtContentPolicy::TYPE_DTD: 568 case ExtContentPolicy::TYPE_FETCH: 569 case ExtContentPolicy::TYPE_FONT: 570 case ExtContentPolicy::TYPE_UA_FONT: 571 case ExtContentPolicy::TYPE_IMAGESET: 572 case ExtContentPolicy::TYPE_OBJECT: 573 case ExtContentPolicy::TYPE_SCRIPT: 574 case ExtContentPolicy::TYPE_STYLESHEET: 575 case ExtContentPolicy::TYPE_SUBDOCUMENT: 576 case ExtContentPolicy::TYPE_PING: 577 case ExtContentPolicy::TYPE_WEB_MANIFEST: 578 case ExtContentPolicy::TYPE_XMLHTTPREQUEST: 579 case ExtContentPolicy::TYPE_XSLT: 580 case ExtContentPolicy::TYPE_OTHER: 581 case ExtContentPolicy::TYPE_SPECULATIVE: 582 case ExtContentPolicy::TYPE_WEB_TRANSPORT: 583 case ExtContentPolicy::TYPE_WEB_IDENTITY: 584 case ExtContentPolicy::TYPE_JSON: 585 break; 586 587 case ExtContentPolicy::TYPE_INVALID: 588 MOZ_ASSERT(false, "Mixed content of unknown type"); 589 // Do not add default: so that compilers can catch the missing case. 590 } 591 592 // Make sure to get the URI the load started with. No need to check 593 // outer schemes because all the wrapping pseudo protocols inherit the 594 // security properties of the actual network request represented 595 // by the innerMost URL. 596 nsCOMPtr<nsIURI> innerContentLocation = NS_GetInnermostURI(aContentLocation); 597 if (!innerContentLocation) { 598 NS_ERROR("Can't get innerURI from aContentLocation"); 599 *aDecision = REJECT_REQUEST; 600 MOZ_LOG(sMCBLog, LogLevel::Verbose, 601 (" -> decision: Request will be rejected because the innermost " 602 "URI could not be " 603 "retrieved")); 604 return NS_OK; 605 } 606 607 // TYPE_IMAGE redirects are cached based on the original URI, not the final 608 // destination and hence cache hits for images may not have the correct 609 // innerContentLocation. Check if the cached hit went through an http 610 // redirect, and if it did, we can't treat this as a secure subresource. 611 if (!aHadInsecureImageRedirect && 612 URISafeToBeLoadedInSecureContext(innerContentLocation)) { 613 *aDecision = ACCEPT; 614 return NS_OK; 615 } 616 617 /* 618 * Most likely aLoadingPrincipal reflects the security context of the owning 619 * document for this mixed content check. There are cases where that is not 620 * true, hence we have to we process requests in the following order: 621 * 1) If the load is triggered by the SystemPrincipal, we allow the load. 622 * Content scripts from addon code do provide aTriggeringPrincipal, which 623 * is an ExpandedPrincipal. If encountered, we allow the load. 624 * 2) If aLoadingPrincipal does not yield to a requestingLocation, then we 625 * fall back to querying the requestingLocation from aTriggeringPrincipal. 626 * 3) If we still end up not having a requestingLocation, we reject the load. 627 */ 628 629 // 1) Check if the load was triggered by the system (SystemPrincipal) or 630 // a content script from addons code (ExpandedPrincipal) in which case the 631 // load is not subject to mixed content blocking. 632 if (triggeringPrincipal) { 633 if (triggeringPrincipal->IsSystemPrincipal()) { 634 *aDecision = ACCEPT; 635 return NS_OK; 636 } 637 nsCOMPtr<nsIExpandedPrincipal> expanded = 638 do_QueryInterface(triggeringPrincipal); 639 if (expanded) { 640 *aDecision = ACCEPT; 641 return NS_OK; 642 } 643 } 644 645 // 2) If aLoadingPrincipal does not provide a requestingLocation, then 646 // we fall back to to querying the requestingLocation from 647 // aTriggeringPrincipal. 648 nsCOMPtr<nsIURI> requestingLocation = 649 GetPrincipalURIOrPrecursorPrincipalURI(loadingPrincipal); 650 if (!requestingLocation) { 651 requestingLocation = 652 GetPrincipalURIOrPrecursorPrincipalURI(triggeringPrincipal); 653 } 654 655 // 3) Giving up. We still don't have a requesting location, therefore we can't 656 // tell if this is a mixed content load. Deny to be safe. 657 if (!requestingLocation) { 658 *aDecision = REJECT_REQUEST; 659 MOZ_LOG(sMCBLog, LogLevel::Verbose, 660 (" -> decision: Request will be rejected because no requesting " 661 "location could be " 662 "gathered.")); 663 return NS_OK; 664 } 665 666 // Check the parent scheme. If it is not an HTTPS or .onion page then mixed 667 // content restrictions do not apply. 668 nsCOMPtr<nsIURI> innerRequestingLocation = 669 NS_GetInnermostURI(requestingLocation); 670 if (!innerRequestingLocation) { 671 NS_ERROR("Can't get innerURI from requestingLocation"); 672 *aDecision = REJECT_REQUEST; 673 MOZ_LOG(sMCBLog, LogLevel::Verbose, 674 (" -> decision: Request will be rejected because the innermost " 675 "URI of the " 676 "requesting location could be gathered.")); 677 return NS_OK; 678 } 679 680 bool parentIsHttps = innerRequestingLocation->SchemeIs("https"); 681 if (!parentIsHttps) { 682 bool parentIsOnion = IsPotentiallyTrustworthyOnion(innerRequestingLocation); 683 if (!parentIsOnion) { 684 *aDecision = ACCEPT; 685 return NS_OK; 686 } 687 } 688 689 bool isHttpScheme = innerContentLocation->SchemeIs("http"); 690 // .onion URLs are encrypted and authenticated. Don't treat them as mixed 691 // content if potentially trustworthy (i.e. whitelisted). 692 if (isHttpScheme && IsPotentiallyTrustworthyOnion(innerContentLocation)) { 693 *aDecision = ACCEPT; 694 MOZ_LOG(sMCBLog, LogLevel::Verbose, 695 (" -> decision: Request will be allowed because the requesting " 696 "location is not using " 697 "HTTPS.")); 698 return NS_OK; 699 } 700 701 // Disallow mixed content loads for workers, shared workers and service 702 // workers. 703 if (isWorkerType) { 704 // For workers, we can assume that we're mixed content at this point, since 705 // the parent is https, and the protocol associated with 706 // innerContentLocation doesn't map to the secure URI flags checked above. 707 // Assert this for sanity's sake 708 #ifdef DEBUG 709 bool isHttpsScheme = innerContentLocation->SchemeIs("https"); 710 MOZ_ASSERT(!isHttpsScheme); 711 #endif 712 *aDecision = REJECT_REQUEST; 713 MOZ_LOG(sMCBLog, LogLevel::Verbose, 714 (" -> decision: Request will be rejected, trying to load a worker " 715 "from an insecure origin.")); 716 return NS_OK; 717 } 718 719 if (isHttpScheme && IsPotentiallyTrustworthyOrigin(innerContentLocation)) { 720 *aDecision = ACCEPT; 721 return NS_OK; 722 } 723 724 // Check if https-only mode upgrades this later anyway 725 if (nsHTTPSOnlyUtils::IsSafeToAcceptCORSOrMixedContent(aLoadInfo)) { 726 *aDecision = ACCEPT; 727 return NS_OK; 728 } 729 730 // The page might have set the CSP directive 'upgrade-insecure-requests'. In 731 // such a case allow the http: load to succeed with the promise that the 732 // channel will get upgraded to https before fetching any data from the 733 // netwerk. Please see: nsHttpChannel::Connect() 734 // 735 // Please note that the CSP directive 'upgrade-insecure-requests' only applies 736 // to http: and ws: (for websockets). Websockets are not subject to mixed 737 // content blocking since insecure websockets are not allowed within secure 738 // pages. Hence, we only have to check against http: here. Skip mixed content 739 // blocking if the subresource load uses http: and the CSP directive 740 // 'upgrade-insecure-requests' is present on the page. 741 742 // Carve-out: if we're in the parent and we're loading media, e.g. through 743 // webbrowserpersist, don't reject it if we can't find a docshell. 744 if (XRE_IsParentProcess() && !requestingWindow && 745 (contentType == ExtContentPolicy::TYPE_IMAGE || 746 contentType == ExtContentPolicy::TYPE_MEDIA)) { 747 *aDecision = ACCEPT; 748 return NS_OK; 749 } 750 // Otherwise, we must have a window 751 NS_ENSURE_TRUE(requestingWindow, NS_OK); 752 753 if (isHttpScheme && aLoadInfo->GetUpgradeInsecureRequests()) { 754 *aDecision = ACCEPT; 755 return NS_OK; 756 } 757 758 // Allow http: mixed content if we are choosing to upgrade them when the 759 // pref "security.mixed_content.upgrade_display_content" is true. 760 // This behaves like GetUpgradeInsecureRequests above in that the channel will 761 // be upgraded to https before fetching any data from the netwerk. 762 if (isHttpScheme) { 763 bool isUpgradableContentType = 764 StaticPrefs::security_mixed_content_upgrade_display_content() && 765 IsUpgradableContentType(internalContentType); 766 if (isUpgradableContentType) { 767 *aDecision = ACCEPT; 768 return NS_OK; 769 } 770 } 771 772 // The page might have set the CSP directive 'block-all-mixed-content' which 773 // should block not only active mixed content loads but in fact all mixed 774 // content loads, see https://www.w3.org/TR/mixed-content/#strict-checking 775 // Block all non secure loads in case the CSP directive is present. Please 776 // note that at this point we already know, based on |schemeSecure| that the 777 // load is not secure, so we can bail out early at this point. 778 if (aLoadInfo->GetBlockAllMixedContent()) { 779 // log a message to the console before returning. 780 nsAutoCString spec; 781 nsresult rv = aContentLocation->GetSpec(spec); 782 NS_ENSURE_SUCCESS(rv, rv); 783 784 AutoTArray<nsString, 1> params; 785 CopyUTF8toUTF16(spec, *params.AppendElement()); 786 787 CSP_LogLocalizedStr("blockAllMixedContent", params, 788 ""_ns, // aSourceFile 789 u""_ns, // aScriptSample 790 0, // aLineNumber 791 1, // aColumnNumber 792 nsIScriptError::errorFlag, "blockAllMixedContent"_ns, 793 requestingWindow->Id(), 794 aLoadInfo->GetOriginAttributes().IsPrivateBrowsing()); 795 *aDecision = REJECT_REQUEST; 796 MOZ_LOG( 797 sMCBLog, LogLevel::Verbose, 798 (" -> decision: Request will be rejected because the CSP directive " 799 "'block-all-mixed-content' was set while trying to load data from " 800 "a non-secure origin.")); 801 return NS_OK; 802 } 803 804 // Determine if the rootDoc is https and if the user decided to allow Mixed 805 // Content 806 WindowContext* topWC = requestingWindow->TopWindowContext(); 807 bool rootHasSecureConnection = topWC->GetIsSecure(); 808 809 // When navigating an iframe, the iframe may be https but its parents may not 810 // be. Check the parents to see if any of them are https. If none of the 811 // parents are https, allow the load. 812 if (contentType == ExtContentPolicyType::TYPE_SUBDOCUMENT && 813 !rootHasSecureConnection && !parentIsHttps) { 814 bool httpsParentExists = false; 815 816 RefPtr<WindowContext> curWindow = requestingWindow; 817 while (!httpsParentExists && curWindow) { 818 httpsParentExists = curWindow->GetIsSecure(); 819 curWindow = curWindow->GetParentWindowContext(); 820 } 821 822 if (!httpsParentExists) { 823 *aDecision = nsIContentPolicy::ACCEPT; 824 return NS_OK; 825 } 826 } 827 828 OriginAttributes originAttributes; 829 if (loadingPrincipal) { 830 originAttributes = loadingPrincipal->OriginAttributesRef(); 831 } else if (triggeringPrincipal) { 832 originAttributes = triggeringPrincipal->OriginAttributesRef(); 833 } 834 835 // At this point we know that the request is mixed content, and the only 836 // question is whether we block it. Record telemetry at this point as to 837 // whether HSTS would have fixed things by making the content location 838 // into an HTTPS URL. 839 // 840 // Note that we count this for redirects as well as primary requests. This 841 // will cause some degree of double-counting, especially when mixed content 842 // is not blocked (e.g., for images). For more detail, see: 843 // https://bugzilla.mozilla.org/show_bug.cgi?id=1198572#c19 844 // 845 // We do not count requests aHadInsecureImageRedirect=true, since these are 846 // just an artifact of the image caching system. 847 bool active = (classification == eMixedScript); 848 if (!aHadInsecureImageRedirect) { 849 if (XRE_IsParentProcess()) { 850 AccumulateMixedContentHSTS(innerContentLocation, active, 851 originAttributes); 852 } else { 853 // Ask the parent process to do the same call 854 mozilla::dom::ContentChild* cc = 855 mozilla::dom::ContentChild::GetSingleton(); 856 if (cc) { 857 cc->SendAccumulateMixedContentHSTS(innerContentLocation, active, 858 originAttributes); 859 } 860 } 861 } 862 863 uint32_t newState = 0; 864 // If the content is display content, and the pref says display content should 865 // be blocked, block it. 866 if (classification == eMixedDisplay) { 867 if (!StaticPrefs::security_mixed_content_block_display_content()) { 868 *aDecision = nsIContentPolicy::ACCEPT; 869 // User has overriden the pref and the root is not https; 870 // mixed display content was allowed on an https subframe. 871 newState |= nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT; 872 } else { 873 *aDecision = nsIContentPolicy::REJECT_REQUEST; 874 MOZ_LOG(sMCBLog, LogLevel::Verbose, 875 (" -> decision: Request will be rejected because the content is " 876 "display " 877 "content (blocked by pref " 878 "security.mixed_content.block_display_content).")); 879 newState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT; 880 } 881 } else { 882 MOZ_ASSERT(classification == eMixedScript); 883 // If the content is active content, and the pref says active content should 884 // be blocked, block it unless the user has choosen to override the pref 885 if (!StaticPrefs::security_mixed_content_block_active_content()) { 886 *aDecision = nsIContentPolicy::ACCEPT; 887 // User has already overriden the pref and the root is not https; 888 // mixed active content was allowed on an https subframe. 889 newState |= nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT; 890 } else { 891 // User has not overriden the pref by Disabling protection. Reject the 892 // request and update the security state. 893 *aDecision = nsIContentPolicy::REJECT_REQUEST; 894 MOZ_LOG(sMCBLog, LogLevel::Verbose, 895 (" -> decision: Request will be rejected because the content is " 896 "active " 897 "content (blocked by pref " 898 "security.mixed_content.block_active_content).")); 899 // The user has not overriden the pref, so make sure they still have an 900 // option by calling nativeDocShell which will invoke the doorhanger 901 newState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT; 902 } 903 } 904 905 // To avoid duplicate errors on the console, we do not report blocked 906 // preloads to the console. 907 if (!isPreload && aReportError) { 908 LogMixedContentMessage(classification, aContentLocation, topWC->Id(), 909 (*aDecision == nsIContentPolicy::REJECT_REQUEST) 910 ? eBlocked 911 : eUserOverride, 912 requestingLocation); 913 } 914 915 // Notify the top WindowContext of the flags we've computed, and it 916 // will handle updating any relevant security UI. 917 topWC->AddSecurityState(newState); 918 return NS_OK; 919 } 920 921 bool nsMixedContentBlocker::URISafeToBeLoadedInSecureContext(nsIURI* aURI) { 922 /* Returns a bool if the URI can be loaded as a sub resource safely. 923 * 924 * Check Protocol Flags to determine if scheme is safe to load: 925 * URI_DOES_NOT_RETURN_DATA - e.g. 926 * "mailto" 927 * URI_IS_LOCAL_RESOURCE - e.g. 928 * "data", 929 * "resource", 930 * "moz-icon" 931 * URI_INHERITS_SECURITY_CONTEXT - e.g. 932 * "javascript" 933 * URI_IS_POTENTIALLY_TRUSTWORTHY - e.g. 934 * "https", 935 * "moz-safe-about" 936 * 937 */ 938 bool schemeLocal = false; 939 bool schemeNoReturnData = false; 940 bool schemeInherits = false; 941 bool schemeSecure = false; 942 if (NS_FAILED(NS_URIChainHasFlags( 943 aURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &schemeLocal)) || 944 NS_FAILED(NS_URIChainHasFlags( 945 aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, 946 &schemeNoReturnData)) || 947 NS_FAILED(NS_URIChainHasFlags( 948 aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, 949 &schemeInherits)) || 950 NS_FAILED(NS_URIChainHasFlags( 951 aURI, nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY, 952 &schemeSecure))) { 953 return false; 954 } 955 956 MOZ_LOG(sMCBLog, LogLevel::Verbose, 957 (" - URISafeToBeLoadedInSecureContext:")); 958 MOZ_LOG(sMCBLog, LogLevel::Verbose, (" - schemeLocal: %i", schemeLocal)); 959 MOZ_LOG(sMCBLog, LogLevel::Verbose, 960 (" - schemeNoReturnData: %i", schemeNoReturnData)); 961 MOZ_LOG(sMCBLog, LogLevel::Verbose, 962 (" - schemeInherits: %i", schemeInherits)); 963 MOZ_LOG(sMCBLog, LogLevel::Verbose, (" - schemeSecure: %i", schemeSecure)); 964 return (schemeLocal || schemeNoReturnData || schemeInherits || schemeSecure); 965 } 966 967 NS_IMETHODIMP 968 nsMixedContentBlocker::ShouldProcess(nsIURI* aContentLocation, 969 nsILoadInfo* aLoadInfo, 970 int16_t* aDecision) { 971 if (!aContentLocation) { 972 // aContentLocation may be null when a plugin is loading without an 973 // associated URI resource 974 if (aLoadInfo->GetExternalContentPolicyType() == 975 ExtContentPolicyType::TYPE_OBJECT) { 976 *aDecision = ACCEPT; 977 return NS_OK; 978 } 979 980 NS_SetRequestBlockingReason(aLoadInfo, 981 nsILoadInfo::BLOCKING_REASON_MIXED_BLOCKED); 982 *aDecision = REJECT_REQUEST; 983 return NS_ERROR_FAILURE; 984 } 985 986 return ShouldLoad(aContentLocation, aLoadInfo, aDecision); 987 } 988 989 // Record information on when HSTS would have made mixed content not mixed 990 // content (regardless of whether it was actually blocked) 991 void nsMixedContentBlocker::AccumulateMixedContentHSTS( 992 nsIURI* aURI, bool aActive, const OriginAttributes& aOriginAttributes) { 993 // This method must only be called in the parent, because 994 // nsSiteSecurityService is only available in the parent 995 if (!XRE_IsParentProcess()) { 996 MOZ_ASSERT(false); 997 return; 998 } 999 1000 bool hsts; 1001 nsresult rv; 1002 nsCOMPtr<nsISiteSecurityService> sss = 1003 do_GetService(NS_SSSERVICE_CONTRACTID, &rv); 1004 if (NS_FAILED(rv)) { 1005 return; 1006 } 1007 rv = sss->IsSecureURI(aURI, aOriginAttributes, &hsts); 1008 if (NS_FAILED(rv)) { 1009 return; 1010 } 1011 1012 // states: would upgrade, would prime, hsts info cached 1013 // active, passive 1014 // 1015 if (!aActive) { 1016 if (!hsts) { 1017 glean::mixed_content::hsts.AccumulateSingleSample( 1018 MCB_HSTS_PASSIVE_NO_HSTS); 1019 } else { 1020 glean::mixed_content::hsts.AccumulateSingleSample( 1021 MCB_HSTS_PASSIVE_WITH_HSTS); 1022 } 1023 } else { 1024 if (!hsts) { 1025 glean::mixed_content::hsts.AccumulateSingleSample( 1026 MCB_HSTS_ACTIVE_NO_HSTS); 1027 } else { 1028 glean::mixed_content::hsts.AccumulateSingleSample( 1029 MCB_HSTS_ACTIVE_WITH_HSTS); 1030 } 1031 } 1032 }