Bug 903966 - Stop blocking 'http://127.0.0.1/' as mixed content. r=ckerschb,kmckinley

According to the spec, content from loopback addresses should no longer
be treated as mixed content even in secure origins. See:
- 349501cdaa
- https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy

Note that we only whitelist '127.0.0.1' and '::1' to match Chrome 53 and
later. See:
- 130ee686fa

It is unclear if HTTPS origins should be able to use workers and WebSocket
connections through a loopback HTTP address. They are not supported in Chrome
(whether this is intentional or not is uncertain) so lets just ignore them for
now.

See also: https://github.com/w3c/web-platform-tests/pull/5304
This commit is contained in:
Birunthan Mohanathas
2017-05-10 20:50:00 +03:00
parent d59935b6b1
commit 409c95124c
8 changed files with 147 additions and 5 deletions

View File

@@ -90,3 +90,8 @@ support-files =
test_no_mcb_on_http_site_font.css
test_no_mcb_on_http_site_font2.html
test_no_mcb_on_http_site_font2.css
[browser_no_mcb_for_loopback.js]
tags = mcb
support-files =
../general/moz.png
test_no_mcb_for_loopback.html

View File

@@ -0,0 +1,56 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// The test loads a HTTPS web page with active content from HTTP loopback URLs
// and makes sure that the mixed content flags on the docshell are not set.
//
// Note that the URLs referenced within the test page intentionally use the
// unassigned port 8 because we don't want to actually load anything, we just
// want to check that the URLs are not blocked.
const TEST_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "test_no_mcb_for_loopback.html";
const LOOPBACK_PNG_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://127.0.0.1:8888") + "moz.png";
const PREF_BLOCK_DISPLAY = "security.mixed_content.block_display_content";
const PREF_BLOCK_ACTIVE = "security.mixed_content.block_active_content";
registerCleanupFunction(function() {
Services.prefs.clearUserPref(PREF_BLOCK_DISPLAY);
Services.prefs.clearUserPref(PREF_BLOCK_ACTIVE);
gBrowser.removeCurrentTab();
});
add_task(function* allowLoopbackMixedContent() {
Services.prefs.setBoolPref(PREF_BLOCK_DISPLAY, true);
Services.prefs.setBoolPref(PREF_BLOCK_ACTIVE, true);
const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
const browser = gBrowser.getBrowserForTab(tab);
yield ContentTask.spawn(browser, null, function() {
is(docShell.hasMixedDisplayContentBlocked, false, "hasMixedDisplayContentBlocked not set");
is(docShell.hasMixedActiveContentBlocked, false, "hasMixedActiveContentBlocked not set");
});
// Check that loopback content served from the cache is not blocked.
yield ContentTask.spawn(browser, LOOPBACK_PNG_URL, function* (loopbackPNGUrl) {
const doc = content.document;
const img = doc.createElement("img");
const promiseImgLoaded = ContentTaskUtils.waitForEvent(img, "load", false);
img.src = loopbackPNGUrl;
Assert.ok(!img.complete, "loopback image not yet loaded");
doc.body.appendChild(img);
yield promiseImgLoaded;
const cachedImg = doc.createElement("img");
cachedImg.src = img.src;
Assert.ok(cachedImg.complete, "loopback image loaded from cache");
});
assertMixedContentBlockingState(browser, {
activeBlocked: false,
activeLoaded: false,
passiveLoaded: false,
});
});

View File

@@ -0,0 +1,50 @@
<!-- See browser_no_mcb_for_localhost.js -->
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf8">
<title>Bug 903966</title>
</head>
<style>
@font-face {
font-family: "Font-IPv4";
src: url("http://127.0.0.1:8/test.ttf");
}
@font-face {
font-family: "Font-IPv6";
src: url("http://[::1]:8/test.ttf");
}
#ip-v4 {
font-family: "Font-IPv4"
}
#ip-v6 {
font-family: "Font-IPv6"
}
</style>
<body>
<div id="ip-v4">test</div>
<div id="ip-v6">test</div>
<img src="http://127.0.0.1:8/test.png">
<img src="http://[::1]:8/test.png">
<iframe src="http://127.0.0.1:8/test.html"></iframe>
<iframe src="http://[::1]:8/test.html"></iframe>
</body>
<script src="http://127.0.0.1:8/test.js"></script>
<script src="http://[::1]:8/test.js"></script>
<link href="http://127.0.0.1:8/test.css" rel="stylesheet"></link>
<link href="http://[::1]:8/test.css" rel="stylesheet"></link>
<script>
fetch("http://127.0.0.1:8");
fetch("http://[::1]:8");
</script>
</html>

View File

@@ -14,6 +14,7 @@
#include "mozilla/dom/AutocompleteErrorEvent.h"
#include "mozilla/dom/nsCSPUtils.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/dom/HTMLFormControlsCollection.h"
#include "mozilla/dom/HTMLFormElementBinding.h"
#include "mozilla/Move.h"
@@ -907,6 +908,10 @@ HTMLFormElement::DoSecureToInsecureSubmitCheck(nsIURI* aActionURL,
return NS_OK;
}
if (nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aActionURL)) {
return NS_OK;
}
nsCOMPtr<nsPIDOMWindowOuter> window = OwnerDoc()->GetWindow();
if (!window) {
return NS_ERROR_FAILURE;

View File

@@ -235,7 +235,8 @@ LOCAL_INCLUDES += [
'/dom/base',
'/dom/canvas',
'/dom/html/input',
'/dom/media/',
'/dom/media',
'/dom/security',
'/dom/xbl',
'/dom/xul',
'/editor/txmgr',

View File

@@ -427,6 +427,18 @@ nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
return rv;
}
bool
nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(nsIURI* aURL) {
nsAutoCString host;
nsresult rv = aURL->GetHost(host);
NS_ENSURE_SUCCESS(rv, false);
// We could also allow 'localhost' (if we can guarantee that it resolves
// to a loopback address), but Chrome doesn't support it as of writing. For
// web compat, lets only allow what Chrome allows.
return host.Equals("127.0.0.1") || host.Equals("::1");
}
/* Static version of ShouldLoad() that contains all the Mixed Content Blocker
* logic. Called from non-static ShouldLoad().
*/
@@ -729,6 +741,18 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
return NS_OK;
}
bool isHttpScheme = false;
rv = innerContentLocation->SchemeIs("http", &isHttpScheme);
NS_ENSURE_SUCCESS(rv, rv);
// Loopback origins are not considered mixed content even over HTTP. See:
// https://w3c.github.io/webappsec-mixed-content/#should-block-fetch
if (isHttpScheme &&
IsPotentiallyTrustworthyLoopbackURL(innerContentLocation)) {
*aDecision = ACCEPT;
return NS_OK;
}
// The page might have set the CSP directive 'upgrade-insecure-requests'. In such
// a case allow the http: load to succeed with the promise that the channel will
// get upgraded to https before fetching any data from the netwerk.
@@ -740,9 +764,6 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
// we only have to check against http: here. Skip mixed content blocking if the
// subresource load uses http: and the CSP directive 'upgrade-insecure-requests'
// is present on the page.
bool isHttpScheme = false;
rv = innerContentLocation->SchemeIs("http", &isHttpScheme);
NS_ENSURE_SUCCESS(rv, rv);
nsIDocument* document = docShell->GetDocument();
MOZ_ASSERT(document, "Expected a document");
if (isHttpScheme && document->GetUpgradeInsecureRequests(isPreload)) {

View File

@@ -45,6 +45,10 @@ public:
nsMixedContentBlocker();
// See:
// https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
static bool IsPotentiallyTrustworthyLoopbackURL(nsIURI* aURL);
/* Static version of ShouldLoad() that contains all the Mixed Content Blocker
* logic. Called from non-static ShouldLoad().
* Called directly from imageLib when an insecure redirect exists in a cached

View File

@@ -14,7 +14,7 @@ add_task(function*() {
// add the top-level server
test_servers['localhost-ip'] = {
host: '127.0.0.1',
host: '127.0.0.2',
response: true,
id: 'localhost-ip',
};