зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1620242 - Basic implementation for HTTPS Only Mode. r=ckerschb,mixedpuppy
Differential Revision: https://phabricator.services.mozilla.com/D62590 --HG-- rename : dom/security/test/csp/file_redirect_report.sjs => dom/security/test/https-only/file_redirect.sjs rename : dom/security/test/csp/file_upgrade_insecure.html => dom/security/test/https-only/file_upgrade_insecure.html rename : dom/security/test/csp/file_upgrade_insecure_server.sjs => dom/security/test/https-only/file_upgrade_insecure_server.sjs rename : dom/security/test/csp/file_upgrade_insecure_wsh.py => dom/security/test/https-only/file_upgrade_insecure_wsh.py extra : moz-landing-system : lando
This commit is contained in:
Родитель
f0d960cc2b
Коммит
e5c3036681
|
@ -253,6 +253,7 @@ module.exports = {
|
|||
"dom/security/test/cors/**",
|
||||
"dom/security/test/csp/**",
|
||||
"dom/security/test/general/**",
|
||||
"dom/security/test/https-only/**",
|
||||
"dom/security/test/mixedcontentblocker/**",
|
||||
"dom/security/test/sri/**",
|
||||
"dom/security/test/referrer-policy/**",
|
||||
|
|
|
@ -142,3 +142,9 @@ XFOInvalid = Invalid X-Frame-Options: “%1$S” header from “%2$S” loaded i
|
|||
XFODeny = Load denied by X-Frame-Options: “%1$S” from “%2$S”, site does not permit any framing. Attempted to load into “%3$S”.
|
||||
# LOCALIZATION NOTE: %1$S is the header value, %2$S is frame URI and %3$S is the parent document URI.
|
||||
XFOSameOrigin = Load denied by X-Frame-Options: “%1$S” from “%2$S”, site does not permit cross-origin framing from “%3$S”.
|
||||
|
||||
# HTTPS-Only Mode
|
||||
# LOCALIZATION NOTE: %1$S is the URL of the upgraded request; %2$S is the upgraded scheme.
|
||||
HTTPSOnlyUpgradeRequest = Upgrading insecure request “%1$S” to use “%2$S”.
|
||||
# LOCALIZATION NOTE: %1$S is the URL of request.
|
||||
HTTPSOnlyNoUpgrade = Request for “%1$S” was not upgraded because it had the NoUpgrade-flag.
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsIParentChannel.h"
|
||||
#include "nsGlobalWindowInner.h"
|
||||
#include "nsContentSecurityUtils.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
|
|
@ -21,6 +21,7 @@ EXPORTS.mozilla.dom += [
|
|||
'nsCSPContext.h',
|
||||
'nsCSPService.h',
|
||||
'nsCSPUtils.h',
|
||||
'nsHTTPSOnlyUtils.h',
|
||||
'nsMixedContentBlocker.h',
|
||||
'PolicyTokenizer.h',
|
||||
'ReferrerInfo.h',
|
||||
|
@ -48,6 +49,7 @@ UNIFIED_SOURCES += [
|
|||
'nsCSPParser.cpp',
|
||||
'nsCSPService.cpp',
|
||||
'nsCSPUtils.cpp',
|
||||
'nsHTTPSOnlyUtils.cpp',
|
||||
'nsMixedContentBlocker.cpp',
|
||||
'PolicyTokenizer.cpp',
|
||||
'ReferrerInfo.cpp',
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsHTTPSOnlyUtils.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIScriptError.h"
|
||||
|
||||
/* static */
|
||||
bool nsHTTPSOnlyUtils::ShouldUpgradeRequest(nsIURI* aURI,
|
||||
nsILoadInfo* aLoadInfo) {
|
||||
// 1. Check if HTTPS-Only mode is enabled
|
||||
if (!mozilla::StaticPrefs::dom_security_https_only_mode()) {
|
||||
return false;
|
||||
}
|
||||
// 2. Check if NoUpgrade-flag is set in LoadInfo
|
||||
if (aLoadInfo->GetHttpsOnlyNoUpgrade()) {
|
||||
// Let's log to the console, that we didn't upgrade this request
|
||||
uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
|
||||
AutoTArray<nsString, 2> params = {
|
||||
NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())};
|
||||
nsHTTPSOnlyUtils::LogLocalizedString(
|
||||
"HTTPSOnlyNoUpgrade", params, nsIScriptError::infoFlag, innerWindowId,
|
||||
!!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId, aURI);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. Upgrade the request
|
||||
|
||||
// Let's log it to the console
|
||||
// Append the additional 's' just for the logging
|
||||
nsAutoCString scheme;
|
||||
aURI->GetScheme(scheme);
|
||||
scheme.AppendLiteral("s");
|
||||
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
|
||||
NS_ConvertUTF8toUTF16 reportScheme(scheme);
|
||||
|
||||
uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
|
||||
AutoTArray<nsString, 2> params = {reportSpec, reportScheme};
|
||||
nsHTTPSOnlyUtils::LogLocalizedString(
|
||||
"HTTPSOnlyUpgradeRequest", params, nsIScriptError::warningFlag,
|
||||
innerWindowId, !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId,
|
||||
aURI);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Logging **/
|
||||
|
||||
/* static */
|
||||
void nsHTTPSOnlyUtils::LogLocalizedString(
|
||||
const char* aName, const nsTArray<nsString>& aParams, uint32_t aFlags,
|
||||
uint64_t aInnerWindowID, bool aFromPrivateWindow, nsIURI* aURI) {
|
||||
nsAutoString logMsg;
|
||||
nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
|
||||
aName, aParams, logMsg);
|
||||
LogMessage(logMsg, aFlags, aInnerWindowID, aFromPrivateWindow, aURI);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags,
|
||||
uint64_t aInnerWindowID,
|
||||
bool aFromPrivateWindow, nsIURI* aURI) {
|
||||
// Prepending HTTPS-Only to the outgoing console message
|
||||
nsString message;
|
||||
message.AppendLiteral(u"HTTPS-Only Mode: ");
|
||||
message.Append(aMessage);
|
||||
|
||||
// Allow for easy distinction in devtools code.
|
||||
nsCString category("HTTPSOnly");
|
||||
|
||||
if (aInnerWindowID > 0) {
|
||||
// Send to content console
|
||||
nsContentUtils::ReportToConsoleByWindowID(message, aFlags, category,
|
||||
aInnerWindowID, aURI);
|
||||
} else {
|
||||
// Send to browser console
|
||||
LogSimpleConsoleError(message, category.get(), aFromPrivateWindow,
|
||||
true /* from chrome context */, aFlags);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsHTTPSOnlyUtils::LogSimpleConsoleError(const nsAString& aErrorText,
|
||||
const char* aCategory,
|
||||
bool aFromPrivateWindow,
|
||||
bool aFromChromeContext,
|
||||
uint32_t aErrorFlags) {
|
||||
nsCOMPtr<nsIScriptError> scriptError =
|
||||
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
|
||||
if (!scriptError) {
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIConsoleService> console =
|
||||
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||
if (!console) {
|
||||
return;
|
||||
}
|
||||
nsresult rv = scriptError->Init(aErrorText, EmptyString(), EmptyString(), 0,
|
||||
0, aErrorFlags, aCategory, aFromPrivateWindow,
|
||||
aFromChromeContext);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
console->LogMessage(scriptError);
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef nsHTTPSOnlyUtils_h___
|
||||
#define nsHTTPSOnlyUtils_h___
|
||||
|
||||
#include "nsIScriptError.h"
|
||||
|
||||
class nsHTTPSOnlyUtils {
|
||||
public:
|
||||
/**
|
||||
* Determines if a request should get because of the HTTPS-Only mode
|
||||
* @param aURI nsIURI of request
|
||||
* @param aLoadInfo nsILoadInfo of request
|
||||
* @param aShouldUpgrade true if request should get upgraded
|
||||
*/
|
||||
static bool ShouldUpgradeRequest(nsIURI* aURI, nsILoadInfo* aLoadInfo);
|
||||
|
||||
/**
|
||||
* Logs localized message to either content console or browser console
|
||||
* @param aName Localization key
|
||||
* @param aParams Localization parameters
|
||||
* @param aFlags Logging Flag (see nsIScriptError)
|
||||
* @param aInnerWindowID Inner Window ID (Logged on browser console if 0)
|
||||
* @param aFromPrivateWindow If from private window
|
||||
* @param [aURI] Optional: URI to log
|
||||
*/
|
||||
static void LogLocalizedString(const char* aName,
|
||||
const nsTArray<nsString>& aParams,
|
||||
uint32_t aFlags, uint64_t aInnerWindowID,
|
||||
bool aFromPrivateWindow,
|
||||
nsIURI* aURI = nullptr);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Logs localized message to either content console or browser console
|
||||
* @param aMessage Message to log
|
||||
* @param aFlags Logging Flag (see nsIScriptError)
|
||||
* @param aInnerWindowID Inner Window ID (Logged on browser console if 0)
|
||||
* @param aFromPrivateWindow If from private window
|
||||
* @param [aURI] Optional: URI to log
|
||||
*/
|
||||
static void LogMessage(const nsAString& aMessage, uint32_t aFlags,
|
||||
uint64_t aInnerWindowID, bool aFromPrivateWindow,
|
||||
nsIURI* aURI = nullptr);
|
||||
|
||||
/**
|
||||
* Report simple error message to the browser console
|
||||
* @param aErrorText the error message
|
||||
* @param aCategory Name of the module reporting error
|
||||
* @param aFromPrivateWindow Whether from private window or not
|
||||
* @param aFromChromeContext Whether from chrome context or not
|
||||
* @param [aErrorFlags] See nsIScriptError.
|
||||
*/
|
||||
static void LogSimpleConsoleError(
|
||||
const nsAString& aErrorText, const char* aCategory,
|
||||
bool aFromPrivateWindow, bool aFromChromeContext,
|
||||
uint32_t aErrorFlags = nsIScriptError::errorFlag);
|
||||
};
|
||||
|
||||
#endif /* nsHTTPSOnlyUtils_h___ */
|
|
@ -19,6 +19,8 @@
|
|||
#include "nsIParentChannel.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsIProtocolHandler.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsISecureBrowserUI.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsLoadGroup.h"
|
||||
|
@ -40,6 +42,7 @@
|
|||
#include "mozilla/net/DNS.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
enum nsMixedContentBlockerMessageType { eBlocked = 0x00, eUserOverride = 0x01 };
|
||||
|
||||
|
@ -812,6 +815,12 @@ nsresult nsMixedContentBlocker::ShouldLoad(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// If https-only mode is enabled we'll upgrade this later anyway
|
||||
if (StaticPrefs::dom_security_https_only_mode()) {
|
||||
*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
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1613063
|
||||
|
||||
// Step 1. Send request with redirect queryString (eg. file_redirect.sjs?302)
|
||||
// Step 2. Server responds with corresponding redirect code to http://example.com/../file_redirect.sjs?check
|
||||
// Step 3. Response from ?check indicates whether the redirected request was secure or not.
|
||||
|
||||
const RESPONSE_SECURE = "secure-ok";
|
||||
const RESPONSE_INSECURE = "secure-error";
|
||||
const RESPONSE_ERROR = "unexpected-query";
|
||||
|
||||
function handleRequest(request, response) {
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
|
||||
const query = request.queryString;
|
||||
|
||||
// Send redirect header
|
||||
if ((query >= 301 && query <= 303) || query == 307) {
|
||||
const loc =
|
||||
"http://example.com/tests/dom/security/test/https-only/file_redirect.sjs?check";
|
||||
response.setStatusLine(request.httpVersion, query, "Moved");
|
||||
response.setHeader("Location", loc, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if scheme is http:// oder https://
|
||||
if (query == "check") {
|
||||
const secure =
|
||||
request.scheme == "https" ? RESPONSE_SECURE : RESPONSE_INSECURE;
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.write(secure);
|
||||
return;
|
||||
}
|
||||
|
||||
// This should not happen
|
||||
response.setStatusLine(request.httpVersion, 500, "OK");
|
||||
response.write(RESPONSE_ERROR);
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1613063 - HTTPS Only Mode</title>
|
||||
<!-- style -->
|
||||
<link rel='stylesheet' type='text/css' href='http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?style' media='screen' />
|
||||
|
||||
<!-- font -->
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "foofont";
|
||||
src: url('http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?font');
|
||||
}
|
||||
.div_foo { font-family: "foofont"; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- images: -->
|
||||
<img src="http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?img"></img>
|
||||
|
||||
<!-- redirects: upgrade http:// to https:// redirect to http:// and then upgrade to https:// again -->
|
||||
<img src="http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?redirect-image"></img>
|
||||
|
||||
<!-- script: -->
|
||||
<script src="http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?script"></script>
|
||||
|
||||
<!-- media: -->
|
||||
<audio src="http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?media"></audio>
|
||||
|
||||
<!-- objects: -->
|
||||
<object width="10" height="10" data="http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?object"></object>
|
||||
|
||||
<!-- font: (apply font loaded in header to div) -->
|
||||
<div class="div_foo">foo</div>
|
||||
|
||||
<!-- iframe: (same origin) -->
|
||||
<iframe src="http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?iframe">
|
||||
<!-- within that iframe we load an image over http and make sure the requested gets upgraded to https -->
|
||||
</iframe>
|
||||
|
||||
<!-- xhr: -->
|
||||
<script type="application/javascript">
|
||||
var myXHR = new XMLHttpRequest();
|
||||
myXHR.open("GET", "http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?xhr");
|
||||
myXHR.send(null);
|
||||
</script>
|
||||
|
||||
<!-- websockets: upgrade ws:// to wss://-->
|
||||
<script type="application/javascript">
|
||||
// WebSocket tests are not supported on Android yet. Bug 1566168
|
||||
const {AppConstants} = SpecialPowers.Cu.import("resource://gre/modules/AppConstants.jsm", {});
|
||||
if (AppConstants.platform !== "android") {
|
||||
var mySocket = new WebSocket("ws://example.com/tests/dom/security/test/https-only/file_upgrade_insecure");
|
||||
mySocket.onopen = function(e) {
|
||||
if (mySocket.url.includes("wss://")) {
|
||||
window.parent.postMessage({result: "websocket-ok"}, "*");
|
||||
}
|
||||
else {
|
||||
window.parent.postMessage({result: "websocket-error"}, "*");
|
||||
}
|
||||
mySocket.close();
|
||||
};
|
||||
mySocket.onerror = function(e) {
|
||||
// debug information for Bug 1316305
|
||||
dump(" xxx mySocket.onerror: (mySocket): " + mySocket + "\n");
|
||||
dump(" xxx mySocket.onerror: (mySocket.url): " + mySocket.url + "\n");
|
||||
dump(" xxx mySocket.onerror: (e): " + e + "\n");
|
||||
dump(" xxx mySocket.onerror: (e.message): " + e.message + "\n");
|
||||
dump(" xxx mySocket.onerror: This might be related to Bug 1316305!\n");
|
||||
window.parent.postMessage({result: "websocket-unexpected-error"}, "*");
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- form action: (upgrade POST from http:// to https://) -->
|
||||
<iframe name='formFrame' id='formFrame'></iframe>
|
||||
<form target="formFrame" action="http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?form" method="POST">
|
||||
<input name="foo" value="foo">
|
||||
<input type="submit" id="submitButton" formenctype='multipart/form-data' value="Submit form">
|
||||
</form>
|
||||
<script type="text/javascript">
|
||||
var submitButton = document.getElementById('submitButton');
|
||||
submitButton.click();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,112 @@
|
|||
// SJS file for HTTPS-Only Mode mochitests
|
||||
// Bug 1613063 - HTTPS Only Mode
|
||||
|
||||
const TOTAL_EXPECTED_REQUESTS = 11;
|
||||
|
||||
const IFRAME_CONTENT =
|
||||
"<!DOCTYPE HTML>" +
|
||||
"<html>" +
|
||||
"<head><meta charset='utf-8'>" +
|
||||
"<title>Bug 1613063 - HTTPS Only Mode</title>" +
|
||||
"</head>" +
|
||||
"<body>" +
|
||||
"<img src='http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?nested-img'></img>" +
|
||||
"</body>" +
|
||||
"</html>";
|
||||
|
||||
const expectedQueries = [
|
||||
"script",
|
||||
"style",
|
||||
"img",
|
||||
"iframe",
|
||||
"form",
|
||||
"xhr",
|
||||
"media",
|
||||
"object",
|
||||
"font",
|
||||
"img-redir",
|
||||
"nested-img",
|
||||
];
|
||||
|
||||
function handleRequest(request, response) {
|
||||
// avoid confusing cache behaviors
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
var queryString = request.queryString;
|
||||
|
||||
// initialize server variables and save the object state
|
||||
// of the initial request, which returns async once the
|
||||
// server has processed all requests.
|
||||
if (queryString == "queryresult") {
|
||||
setState("totaltests", TOTAL_EXPECTED_REQUESTS.toString());
|
||||
setState("receivedQueries", "");
|
||||
response.processAsync();
|
||||
setObjectState("queryResult", response);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle img redirect (https->http)
|
||||
if (queryString == "redirect-image") {
|
||||
var newLocation =
|
||||
"http://example.com/tests/dom/security/test/https-only/file_upgrade_insecure_server.sjs?img-redir";
|
||||
response.setStatusLine("1.1", 302, "Found");
|
||||
response.setHeader("Location", newLocation, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// just in case error handling for unexpected queries
|
||||
if (expectedQueries.indexOf(queryString) == -1) {
|
||||
response.write("unexpected-response");
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure all the requested queries are indeed https
|
||||
queryString += request.scheme == "https" ? "-ok" : "-error";
|
||||
|
||||
var receivedQueries = getState("receivedQueries");
|
||||
|
||||
// images, scripts, etc. get queried twice, do not
|
||||
// confuse the server by storing the preload as
|
||||
// well as the actual load. If either the preload
|
||||
// or the actual load is not https, then we would
|
||||
// append "-error" in the array and the test would
|
||||
// fail at the end.
|
||||
if (receivedQueries.includes(queryString)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// append the result to the total query string array
|
||||
if (receivedQueries != "") {
|
||||
receivedQueries += ",";
|
||||
}
|
||||
receivedQueries += queryString;
|
||||
setState("receivedQueries", receivedQueries);
|
||||
|
||||
// keep track of how many more requests the server
|
||||
// is expecting
|
||||
var totaltests = parseInt(getState("totaltests"));
|
||||
totaltests -= 1;
|
||||
setState("totaltests", totaltests.toString());
|
||||
|
||||
// return content (img) for the nested iframe to test
|
||||
// that subresource requests within nested contexts
|
||||
// get upgraded as well. We also have to return
|
||||
// the iframe context in case of an error so we
|
||||
// can test both, using upgrade-insecure as well
|
||||
// as the base case of not using upgrade-insecure.
|
||||
if (queryString == "iframe-ok" || queryString == "iframe-error") {
|
||||
response.write(IFRAME_CONTENT);
|
||||
}
|
||||
|
||||
// if we have received all the requests, we return
|
||||
// the result back.
|
||||
if (totaltests == 0) {
|
||||
getObjectState("queryResult", function(queryResponse) {
|
||||
if (!queryResponse) {
|
||||
return;
|
||||
}
|
||||
var receivedQueries = getState("receivedQueries");
|
||||
queryResponse.write(receivedQueries);
|
||||
queryResponse.finish();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
from mod_pywebsocket import msgutil
|
||||
|
||||
def web_socket_do_extra_handshake(request):
|
||||
pass
|
||||
|
||||
def web_socket_transfer_data(request):
|
||||
pass
|
|
@ -0,0 +1,13 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_redirect.sjs
|
||||
file_upgrade_insecure.html
|
||||
file_upgrade_insecure_server.sjs
|
||||
file_upgrade_insecure_wsh.py
|
||||
prefs =
|
||||
security.mixed_content.upgrade_display_content=false
|
||||
|
||||
[test_resource_upgrade.html]
|
||||
scheme=https
|
||||
[test_redirect_upgrade.html]
|
||||
scheme=https
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1613063
|
||||
Test that 302 redirect requests get upgraded to https:// with HTTPS-Only Mode enabled
|
||||
-->
|
||||
|
||||
<head>
|
||||
<title>HTTPS-Only Mode - XHR Redirect Upgrade</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>HTTPS-Only Mode</h1>
|
||||
<p>Upgrade Test for insecure XHR redirects.</p>
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1613063">Bug 1613063</a>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
const redirectCodes = ["301", "302", "303", "307"]
|
||||
let currentTest = 0
|
||||
|
||||
function startTest() {
|
||||
const currentCode = redirectCodes[currentTest];
|
||||
|
||||
const myXHR = new XMLHttpRequest();
|
||||
// Make a request to a site (eg. https://file_redirect.sjs?301), which will redirect to http://file_redirect.sjs?check.
|
||||
// The response will either be secure-ok, if the request has been upgraded to https:// or secure-error if it didn't.
|
||||
myXHR.open("GET", `https://example.com/tests/dom/security/test/https-only/file_redirect.sjs?${currentCode}`);
|
||||
myXHR.onload = (e) => {
|
||||
is(myXHR.responseText, "secure-ok", `a ${currentCode} redirect when posting violation report should be blocked`)
|
||||
testDone();
|
||||
}
|
||||
// This should not happen
|
||||
myXHR.onerror = (e) => {
|
||||
ok(false, `Could not query results from server for ${currentCode}-redirect test (" + e.message + ")`);
|
||||
testDone();
|
||||
}
|
||||
myXHR.send();
|
||||
}
|
||||
|
||||
function testDone() {
|
||||
// Check if there are remaining tests
|
||||
if (++currentTest < redirectCodes.length) {
|
||||
startTest()
|
||||
} else {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
// Set preference and start test
|
||||
SpecialPowers.pushPrefEnv({ set: [["dom.security.https_only_mode", true]] }, startTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,123 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>HTTPS-Only Mode - Resource Upgrade</title>
|
||||
<!-- Including SimpleTest.js so we can use waitForExplicitFinish !-->
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>HTTPS-Only Mode</h1>
|
||||
<p>Upgrade Test for various resources</p>
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1613063">Bug 1613063</a>
|
||||
<iframe style="width:100%;" id="testframe"></iframe>
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
/* Description of the test:
|
||||
* We load resources (img, script, sytle, etc) over *http* and make sure
|
||||
* that all the resources get upgraded to use >> https << when the
|
||||
* preference "dom.security.https_only_mode" is set to true. We further
|
||||
* test that subresources within nested contexts (iframes) get upgraded
|
||||
* and also test the handling of server side redirects.
|
||||
*
|
||||
* In detail:
|
||||
* We perform an XHR request to the *.sjs file which is processed async on
|
||||
* the server and waits till all the requests were processed by the server.
|
||||
* Once the server received all the different requests, the server responds
|
||||
* to the initial XHR request with an array of results which must match
|
||||
* the expected results from each test, making sure that all requests
|
||||
* received by the server (*.sjs) were actually *https* requests.
|
||||
*/
|
||||
|
||||
const { AppConstants } = SpecialPowers.Cu.import(
|
||||
"resource://gre/modules/AppConstants.jsm",
|
||||
{}
|
||||
);
|
||||
const splitRegex = /^(.*)-(.*)$/
|
||||
const testConfig = {
|
||||
topLevelScheme: "http://",
|
||||
results: [
|
||||
"iframe", "script", "img", "img-redir", "font", "xhr", "style",
|
||||
"media", "object", "form", "nested-img"
|
||||
]
|
||||
}
|
||||
// TODO: WebSocket tests are not supported on Android Yet. Bug 1566168.
|
||||
if (AppConstants.platform !== "android") {
|
||||
testConfig.results.push("websocket");
|
||||
}
|
||||
|
||||
|
||||
function runTest() {
|
||||
// sends an xhr request to the server which is processed async, which only
|
||||
// returns after the server has received all the expected requests.
|
||||
var myXHR = new XMLHttpRequest();
|
||||
myXHR.open("GET", "file_upgrade_insecure_server.sjs?queryresult");
|
||||
myXHR.onload = function (e) {
|
||||
var results = myXHR.responseText.split(",");
|
||||
for (var index in results) {
|
||||
checkResult(results[index]);
|
||||
}
|
||||
}
|
||||
myXHR.onerror = function (e) {
|
||||
ok(false, "Could not query results from server (" + e.message + ")");
|
||||
finishTest();
|
||||
}
|
||||
myXHR.send();
|
||||
|
||||
// give it some time and run the testpage
|
||||
SimpleTest.executeSoon(() => {
|
||||
var src = testConfig.topLevelScheme + "example.com/tests/dom/security/test/https-only/file_upgrade_insecure.html";
|
||||
document.getElementById("testframe").src = src;
|
||||
});
|
||||
}
|
||||
|
||||
// a postMessage handler that is used by sandboxed iframes without
|
||||
// 'allow-same-origin' to bubble up results back to this main page.
|
||||
window.addEventListener("message", receiveMessage);
|
||||
function receiveMessage(event) {
|
||||
checkResult(event.data.result);
|
||||
}
|
||||
|
||||
function finishTest() {
|
||||
window.removeEventListener("message", receiveMessage);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function checkResult(response) {
|
||||
// A response looks either like this "iframe-ok" or "[key]-[result]"
|
||||
const [, key, result] = splitRegex.exec(response)
|
||||
// try to find the expected result within the results array
|
||||
var index = testConfig.results.indexOf(key);
|
||||
|
||||
// If the response is not even part of the results array, something is super wrong
|
||||
if (index == -1) {
|
||||
ok(false, `Unexpected response from server (${response})`);
|
||||
finishTest();
|
||||
}
|
||||
|
||||
// take the element out the array and continue till the results array is empty
|
||||
if (index != -1) {
|
||||
testConfig.results.splice(index, 1);
|
||||
}
|
||||
|
||||
// Check if the result was okay or had an error
|
||||
is(result, 'ok', `Upgrade all requests on toplevel http for '${key}' came back with: '${result}'`)
|
||||
|
||||
// If we're not expecting any more resulsts, finish the test
|
||||
if (testConfig.results.length == 0) {
|
||||
finishTest();
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Set preference and start test
|
||||
SpecialPowers.pushPrefEnv({ set: [["dom.security.https_only_mode", true]] }, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -19,6 +19,7 @@ MOCHITEST_MANIFESTS += [
|
|||
'cors/mochitest.ini',
|
||||
'csp/mochitest.ini',
|
||||
'general/mochitest.ini',
|
||||
'https-only/mochitest.ini',
|
||||
'mixedcontentblocker/mochitest.ini',
|
||||
'referrer-policy/mochitest.ini',
|
||||
'sri/mochitest.ini',
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "mozilla/dom/MessageEventBinding.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
#include "mozilla/dom/nsCSPUtils.h"
|
||||
#include "mozilla/dom/nsHTTPSOnlyUtils.h"
|
||||
#include "mozilla/dom/nsMixedContentBlocker.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/SerializedStackHolder.h"
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include "mozilla/dom/WorkerRef.h"
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
@ -1578,6 +1580,26 @@ nsresult WebSocketImpl::Init(JSContext* aCx, nsIPrincipal* aLoadingPrincipal,
|
|||
}
|
||||
}
|
||||
|
||||
// If the HTTPS-Only mode is enabled, we need to upgrade the websocket
|
||||
// connection from ws:// to wss:// and mark it as secure.
|
||||
if (!mIsServerSide && !mSecure &&
|
||||
StaticPrefs::dom_security_https_only_mode()) {
|
||||
// let's use the old specification before the upgrade for logging
|
||||
AutoTArray<nsString, 2> params;
|
||||
CopyUTF8toUTF16(mURI, *params.AppendElement());
|
||||
|
||||
mURI.ReplaceSubstring("ws://", "wss://");
|
||||
if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
|
||||
return NS_OK;
|
||||
}
|
||||
mSecure = true;
|
||||
|
||||
params.AppendElement(NS_LITERAL_STRING("wss"));
|
||||
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyUpgradeInsecureRequest",
|
||||
params, nsIScriptError::warningFlag,
|
||||
mInnerWindowID, mPrivateBrowsing);
|
||||
}
|
||||
|
||||
// Potentially the page uses the CSP directive 'upgrade-insecure-requests'.
|
||||
// In such a case we have to upgrade ws: to wss: and also update mSecure
|
||||
// to reflect that upgrade. Please note that we can not upgrade from ws:
|
||||
|
|
|
@ -580,6 +580,7 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadInfo* aLoadInfo,
|
|||
aLoadInfo->GetDocumentHasLoaded(),
|
||||
aLoadInfo->GetAllowListFutureDocumentsCreatedFromThisRedirectChain(),
|
||||
cspNonce, aLoadInfo->GetSkipContentSniffing(),
|
||||
aLoadInfo->GetHttpsOnlyNoUpgrade(),
|
||||
aLoadInfo->GetIsFromProcessingFrameAttributes(), cookieJarSettingsArgs,
|
||||
aLoadInfo->GetRequestBlockingReason(), maybeCspToInheritInfo));
|
||||
|
||||
|
@ -777,7 +778,8 @@ nsresult LoadInfoArgsToLoadInfo(
|
|||
loadInfoArgs.documentHasLoaded(),
|
||||
loadInfoArgs.allowListFutureDocumentsCreatedFromThisRedirectChain(),
|
||||
loadInfoArgs.cspNonce(), loadInfoArgs.skipContentSniffing(),
|
||||
loadInfoArgs.requestBlockingReason(), loadingContext);
|
||||
loadInfoArgs.httpsOnlyNoUpgrade(), loadInfoArgs.requestBlockingReason(),
|
||||
loadingContext);
|
||||
|
||||
if (loadInfoArgs.isFromProcessingFrameAttributes()) {
|
||||
loadInfo->SetIsFromProcessingFrameAttributes();
|
||||
|
@ -793,6 +795,7 @@ void LoadInfoToParentLoadInfoForwarder(
|
|||
*aForwarderArgsOut = ParentLoadInfoForwarderArgs(
|
||||
false, false, Nothing(), nsILoadInfo::TAINTING_BASIC,
|
||||
false, // SkipContentSniffing
|
||||
false, // HttpsOnlyNoUpgrade
|
||||
false, // serviceWorkerTaintingSynthesized
|
||||
false, // documentHasUserInteracted
|
||||
false, // documentHasLoaded
|
||||
|
@ -827,7 +830,7 @@ void LoadInfoToParentLoadInfoForwarder(
|
|||
*aForwarderArgsOut = ParentLoadInfoForwarderArgs(
|
||||
aLoadInfo->GetAllowInsecureRedirectToDataURI(),
|
||||
aLoadInfo->GetBypassCORSChecks(), ipcController, tainting,
|
||||
aLoadInfo->GetSkipContentSniffing(),
|
||||
aLoadInfo->GetSkipContentSniffing(), aLoadInfo->GetHttpsOnlyNoUpgrade(),
|
||||
aLoadInfo->GetServiceWorkerTaintingSynthesized(),
|
||||
aLoadInfo->GetDocumentHasUserInteracted(),
|
||||
aLoadInfo->GetDocumentHasLoaded(),
|
||||
|
@ -866,6 +869,9 @@ nsresult MergeParentLoadInfoForwarder(
|
|||
rv = aLoadInfo->SetSkipContentSniffing(aForwarderArgs.skipContentSniffing());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = aLoadInfo->SetHttpsOnlyNoUpgrade(aForwarderArgs.httpsOnlyNoUpgrade());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasUserInteracted(
|
||||
aForwarderArgs.documentHasUserInteracted()));
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
|
|
|
@ -2406,6 +2406,13 @@
|
|||
value: false
|
||||
mirror: always
|
||||
|
||||
# If true, all content requests will get upgraded to HTTPS://
|
||||
# (some Firefox functionality requests, like OCSP will not be affected)
|
||||
- name: dom.security.https_only_mode
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# Is support for selection event APIs enabled?
|
||||
- name: dom.select_events.enabled
|
||||
type: bool
|
||||
|
|
|
@ -102,6 +102,7 @@ LoadInfo::LoadInfo(
|
|||
mDocumentHasLoaded(false),
|
||||
mAllowListFutureDocumentsCreatedFromThisRedirectChain(false),
|
||||
mSkipContentSniffing(false),
|
||||
mHttpsOnlyNoUpgrade(false),
|
||||
mIsFromProcessingFrameAttributes(false) {
|
||||
MOZ_ASSERT(mLoadingPrincipal);
|
||||
MOZ_ASSERT(mTriggeringPrincipal);
|
||||
|
@ -364,6 +365,7 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow,
|
|||
mDocumentHasLoaded(false),
|
||||
mAllowListFutureDocumentsCreatedFromThisRedirectChain(false),
|
||||
mSkipContentSniffing(false),
|
||||
mHttpsOnlyNoUpgrade(false),
|
||||
mIsFromProcessingFrameAttributes(false) {
|
||||
// Top-level loads are never third-party
|
||||
// Grab the information we can out of the window.
|
||||
|
@ -464,6 +466,7 @@ LoadInfo::LoadInfo(dom::CanonicalBrowsingContext* aBrowsingContext,
|
|||
mDocumentHasLoaded(false),
|
||||
mAllowListFutureDocumentsCreatedFromThisRedirectChain(false),
|
||||
mSkipContentSniffing(false),
|
||||
mHttpsOnlyNoUpgrade(false),
|
||||
mIsFromProcessingFrameAttributes(false) {
|
||||
// Top-level loads are never third-party
|
||||
// Grab the information we can out of the window.
|
||||
|
@ -564,6 +567,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
|
|||
rhs.mAllowListFutureDocumentsCreatedFromThisRedirectChain),
|
||||
mCspNonce(rhs.mCspNonce),
|
||||
mSkipContentSniffing(rhs.mSkipContentSniffing),
|
||||
mHttpsOnlyNoUpgrade(rhs.mHttpsOnlyNoUpgrade),
|
||||
mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes) {}
|
||||
|
||||
LoadInfo::LoadInfo(
|
||||
|
@ -601,7 +605,8 @@ LoadInfo::LoadInfo(
|
|||
bool aDocumentHasLoaded,
|
||||
bool aAllowListFutureDocumentsCreatedFromThisRedirectChain,
|
||||
const nsAString& aCspNonce, bool aSkipContentSniffing,
|
||||
uint32_t aRequestBlockingReason, nsINode* aLoadingContext)
|
||||
bool aHttpsOnlyNoUpgrade, uint32_t aRequestBlockingReason,
|
||||
nsINode* aLoadingContext)
|
||||
: mLoadingPrincipal(aLoadingPrincipal),
|
||||
mTriggeringPrincipal(aTriggeringPrincipal),
|
||||
mPrincipalToInherit(aPrincipalToInherit),
|
||||
|
@ -657,6 +662,7 @@ LoadInfo::LoadInfo(
|
|||
aAllowListFutureDocumentsCreatedFromThisRedirectChain),
|
||||
mCspNonce(aCspNonce),
|
||||
mSkipContentSniffing(aSkipContentSniffing),
|
||||
mHttpsOnlyNoUpgrade(aHttpsOnlyNoUpgrade),
|
||||
mIsFromProcessingFrameAttributes(false) {
|
||||
// Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
|
||||
MOZ_ASSERT(mLoadingPrincipal ||
|
||||
|
@ -1462,6 +1468,18 @@ LoadInfo::SetSkipContentSniffing(bool aSkipContentSniffing) {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadInfo::GetHttpsOnlyNoUpgrade(bool* aHttpsOnlyNoUpgrade) {
|
||||
*aHttpsOnlyNoUpgrade = mHttpsOnlyNoUpgrade;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadInfo::SetHttpsOnlyNoUpgrade(bool aHttpsOnlyNoUpgrade) {
|
||||
mHttpsOnlyNoUpgrade = aHttpsOnlyNoUpgrade;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
LoadInfo::GetIsTopLevelLoad(bool* aResult) {
|
||||
*aResult = mFrameOuterWindowID ? mFrameOuterWindowID == mOuterWindowID
|
||||
|
|
|
@ -163,7 +163,8 @@ class LoadInfo final : public nsILoadInfo {
|
|||
bool aDocumentHasUserInteracted, bool aDocumentHasLoaded,
|
||||
bool aAllowListFutureDocumentsCreatedFromThisRedirectChain,
|
||||
const nsAString& aCspNonce, bool aSkipContentSniffing,
|
||||
uint32_t aRequestBlockingReason, nsINode* aLoadingContext);
|
||||
bool aHttpsOnlyNoUpgrade, uint32_t aRequestBlockingReason,
|
||||
nsINode* aLoadingContext);
|
||||
LoadInfo(const LoadInfo& rhs);
|
||||
|
||||
NS_IMETHOD GetRedirects(JSContext* aCx,
|
||||
|
@ -258,6 +259,7 @@ class LoadInfo final : public nsILoadInfo {
|
|||
bool mAllowListFutureDocumentsCreatedFromThisRedirectChain;
|
||||
nsString mCspNonce;
|
||||
bool mSkipContentSniffing;
|
||||
bool mHttpsOnlyNoUpgrade;
|
||||
|
||||
// Is true if this load was triggered by processing the attributes of the
|
||||
// browsing context container.
|
||||
|
|
|
@ -206,6 +206,12 @@ static inline already_AddRefed<nsIChannel> SetupIPCheckChannel(bool ipv4) {
|
|||
|
||||
channel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
|
||||
|
||||
{
|
||||
// Prevent HTTPS-Only Mode from upgrading the OCSP request.
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
||||
loadInfo->SetHttpsOnlyNoUpgrade(true);
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(channel);
|
||||
|
|
|
@ -407,7 +407,6 @@ interface nsILoadInfo : nsISupports
|
|||
*/
|
||||
[infallible] readonly attribute unsigned long securityMode;
|
||||
|
||||
|
||||
/**
|
||||
* This flag is used for any browsing context where we should not sniff
|
||||
* the content type. E.g if an iframe has the XCTO nosniff header, then
|
||||
|
@ -416,6 +415,12 @@ interface nsILoadInfo : nsISupports
|
|||
*/
|
||||
[infallible] attribute boolean skipContentSniffing;
|
||||
|
||||
/**
|
||||
* If httpsOnlyNoUpgrade is true, the request won't get upgraded by the
|
||||
* HTTPS-Only Mode.
|
||||
*/
|
||||
[infallible] attribute boolean httpsOnlyNoUpgrade;
|
||||
|
||||
/**
|
||||
* True if this request is embedded in a context that can't be third-party
|
||||
* (i.e. an iframe embedded in a cross-origin parent window). If this is
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
#include "plstr.h"
|
||||
#include "nsINestedURI.h"
|
||||
#include "mozilla/dom/nsCSPUtils.h"
|
||||
#include "mozilla/dom/nsHTTPSOnlyUtils.h"
|
||||
#include "mozilla/dom/nsMixedContentBlocker.h"
|
||||
#include "mozilla/dom/BlobURLProtocolHandler.h"
|
||||
#include "mozilla/net/HttpBaseChannel.h"
|
||||
|
@ -2839,6 +2840,12 @@ nsresult NS_ShouldSecureUpgrade(
|
|||
if (!isHttps &&
|
||||
!nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) {
|
||||
if (aLoadInfo) {
|
||||
// Check if the request can get upgraded with the HTTPS-Only mode
|
||||
if (nsHTTPSOnlyUtils::ShouldUpgradeRequest(aURI, aLoadInfo)) {
|
||||
aShouldUpgrade = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If any of the documents up the chain to the root document makes use of
|
||||
// the CSP directive 'upgrade-insecure-requests', then it's time to
|
||||
// fulfill the promise to CSP and mixed content blocking to upgrade the
|
||||
|
|
|
@ -144,6 +144,7 @@ struct LoadInfoArgs
|
|||
bool allowListFutureDocumentsCreatedFromThisRedirectChain;
|
||||
nsString cspNonce;
|
||||
bool skipContentSniffing;
|
||||
bool httpsOnlyNoUpgrade;
|
||||
bool isFromProcessingFrameAttributes;
|
||||
CookieJarSettingsArgs cookieJarSettings;
|
||||
uint32_t requestBlockingReason;
|
||||
|
@ -177,12 +178,15 @@ struct ParentLoadInfoForwarderArgs
|
|||
// tainting value.
|
||||
uint32_t tainting;
|
||||
|
||||
|
||||
// This flag is used for any browsing context where we should not sniff
|
||||
// the content type. E.g if an iframe has the XCTO nosniff header, then
|
||||
// that flag is set to true so we skip content sniffing for that browsing
|
||||
bool skipContentSniffing;
|
||||
|
||||
// If httpsOnlyNoUpgrade is true, the request won't get upgraded by the
|
||||
// HTTPS-Only Mode.
|
||||
bool httpsOnlyNoUpgrade;
|
||||
|
||||
// We must also note that the tainting value was explicitly set
|
||||
// by the service worker.
|
||||
bool serviceWorkerTaintingSynthesized;
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsQueryObject.h"
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -774,17 +775,17 @@ nsCORSListenerProxy::CheckListenerChain() {
|
|||
return retargetableListener->CheckListenerChain();
|
||||
}
|
||||
|
||||
// Please note that the CSP directive 'upgrade-insecure-requests' relies
|
||||
// on the promise that channels get updated from http: to https: before
|
||||
// the channel fetches any data from the netwerk. Such channels should
|
||||
// not be blocked by CORS and marked as cross origin requests. E.g.:
|
||||
// toplevel page: https://www.example.com loads
|
||||
// xhr: http://www.example.com/foo which gets updated to
|
||||
// https://www.example.com/foo
|
||||
// Please note that the CSP directive 'upgrade-insecure-requests' and the
|
||||
// HTTPS-Only Mode are relying on the promise that channels get updated from
|
||||
// http: to https: before the channel fetches any data from the netwerk. Such
|
||||
// channels should not be blocked by CORS and marked as cross origin requests.
|
||||
// E.g.: toplevel page: https://www.example.com loads
|
||||
// xhr: http://www.example.com/foo which gets updated to
|
||||
// https://www.example.com/foo
|
||||
// In such a case we should bail out of CORS and rely on the promise that
|
||||
// nsHttpChannel::Connect() upgrades the request from http to https.
|
||||
bool CheckUpgradeInsecureRequestsPreventsCORS(
|
||||
nsIPrincipal* aRequestingPrincipal, nsIChannel* aChannel) {
|
||||
bool CheckInsecureUpgradePreventsCORS(nsIPrincipal* aRequestingPrincipal,
|
||||
nsIChannel* aChannel) {
|
||||
nsCOMPtr<nsIURI> channelURI;
|
||||
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
@ -817,11 +818,7 @@ bool CheckUpgradeInsecureRequestsPreventsCORS(
|
|||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
||||
// lets see if the loadInfo indicates that the request will
|
||||
// be upgraded before fetching any data from the netwerk.
|
||||
return loadInfo->GetUpgradeInsecureRequests() ||
|
||||
loadInfo->GetBrowserUpgradeInsecureRequests();
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
|
||||
|
@ -879,16 +876,24 @@ nsresult nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// if the CSP directive 'upgrade-insecure-requests' is used then we should
|
||||
// not incorrectly require CORS if the only difference of a subresource
|
||||
// request and the main page is the scheme.
|
||||
// e.g. toplevel page: https://www.example.com loads
|
||||
// xhr: http://www.example.com/somefoo,
|
||||
// If the CSP directive 'upgrade-insecure-requests' is used or the HTTPS-Only
|
||||
// Mode is enabled then we should not incorrectly require CORS if the only
|
||||
// difference of a subresource request and the main page is the scheme. e.g.
|
||||
// toplevel page: https://www.example.com loads
|
||||
// xhr: http://www.example.com/somefoo,
|
||||
// then the xhr request will be upgraded to https before it fetches any data
|
||||
// from the netwerk, hence we shouldn't require CORS in that specific case.
|
||||
if (CheckUpgradeInsecureRequestsPreventsCORS(mRequestingPrincipal,
|
||||
aChannel)) {
|
||||
return NS_OK;
|
||||
if (CheckInsecureUpgradePreventsCORS(mRequestingPrincipal, aChannel)) {
|
||||
// Check if HTTPS-Only Mode is enabled
|
||||
if (!loadInfo->GetHttpsOnlyNoUpgrade() &&
|
||||
StaticPrefs::dom_security_https_only_mode()) {
|
||||
return NS_OK;
|
||||
}
|
||||
// Check if 'upgrade-insecure-requests' is used
|
||||
if (loadInfo->GetUpgradeInsecureRequests() ||
|
||||
loadInfo->GetBrowserUpgradeInsecureRequests()) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we need to do a preflight, and if so set one up. This must be
|
||||
|
|
|
@ -265,6 +265,11 @@ OCSPRequest::Run() {
|
|||
nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
|
||||
nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
||||
|
||||
// Prevent HTTPS-Only Mode from upgrading the OCSP request.
|
||||
loadInfo->SetHttpsOnlyNoUpgrade(true);
|
||||
|
||||
// For OCSP requests, only the first party domain and private browsing id
|
||||
// aspects of origin attributes are used. This means that:
|
||||
// a) if first party isolation is enabled, OCSP requests will be isolated
|
||||
|
@ -277,7 +282,6 @@ OCSPRequest::Run() {
|
|||
attrs.mFirstPartyDomain = mOriginAttributes.mFirstPartyDomain;
|
||||
attrs.mPrivateBrowsingId = mOriginAttributes.mPrivateBrowsingId;
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
||||
rv = loadInfo->SetOriginAttributes(attrs);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NotifyDone(rv, lock);
|
||||
|
|
|
@ -37,6 +37,8 @@ function URLFetcher(url, timeout) {
|
|||
xhr.channel.setTRRMode(Ci.nsIRequest.TRR_DISABLED_MODE);
|
||||
// We except this from being classified
|
||||
xhr.channel.loadFlags |= Ci.nsIChannel.LOAD_BYPASS_URL_CLASSIFIER;
|
||||
// Prevent HTTPS-Only Mode from upgrading the request.
|
||||
xhr.channel.loadInfo.httpsOnlyNoUpgrade = true;
|
||||
|
||||
// We don't want to follow _any_ redirects
|
||||
xhr.channel.QueryInterface(Ci.nsIHttpChannel).redirectionLimit = 0;
|
||||
|
|
|
@ -395,6 +395,9 @@ GMPAddon.prototype = {
|
|||
get isEME() {
|
||||
return this.id == "gmp-widevinecdm" || this.id.indexOf("gmp-eme-") == 0;
|
||||
},
|
||||
get isOpenH264() {
|
||||
return this.id == "gmp-gmpopenh264";
|
||||
},
|
||||
/**
|
||||
* @return true if the addon has been previously installed and this is
|
||||
* a new version, if this is a fresh install return false
|
||||
|
@ -475,31 +478,38 @@ GMPDownloader.prototype = {
|
|||
type: "downloaderr",
|
||||
});
|
||||
}
|
||||
|
||||
return ProductAddonChecker.downloadAddon(gmpAddon).then(zipPath => {
|
||||
let relativePath = OS.Path.join(gmpAddon.id, gmpAddon.version);
|
||||
log.info("install to directory path: " + relativePath);
|
||||
let gmpInstaller = new GMPExtractor(zipPath, relativePath);
|
||||
let installPromise = gmpInstaller.install();
|
||||
return installPromise.then(extractedPaths => {
|
||||
// Success, set the prefs
|
||||
let now = Math.round(Date.now() / 1000);
|
||||
GMPPrefs.setInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, now, gmpAddon.id);
|
||||
// Remember our ABI, so that if the profile is migrated to another
|
||||
// platform or from 32 -> 64 bit, we notice and don't try to load the
|
||||
// unexecutable plugin library.
|
||||
let abi = GMPUtils._expectedABI(gmpAddon);
|
||||
log.info("Setting ABI to '" + abi + "' for " + gmpAddon.id);
|
||||
GMPPrefs.setString(GMPPrefs.KEY_PLUGIN_ABI, abi, gmpAddon.id);
|
||||
// Setting the version pref signals installation completion to consumers,
|
||||
// if you need to set other prefs etc. do it before this.
|
||||
GMPPrefs.setString(
|
||||
GMPPrefs.KEY_PLUGIN_VERSION,
|
||||
gmpAddon.version,
|
||||
gmpAddon.id
|
||||
);
|
||||
return extractedPaths;
|
||||
});
|
||||
});
|
||||
// If the HTTPS-Only Mode is enabled, every insecure request gets upgraded
|
||||
// by default. This upgrade has to be prevented for openh264 downloads since
|
||||
// the server doesn't support https://
|
||||
const downloadOptions = {
|
||||
httpsOnlyNoUpgrade: gmpAddon.isOpenH264,
|
||||
};
|
||||
return ProductAddonChecker.downloadAddon(gmpAddon, downloadOptions).then(
|
||||
zipPath => {
|
||||
let relativePath = OS.Path.join(gmpAddon.id, gmpAddon.version);
|
||||
log.info("install to directory path: " + relativePath);
|
||||
let gmpInstaller = new GMPExtractor(zipPath, relativePath);
|
||||
let installPromise = gmpInstaller.install();
|
||||
return installPromise.then(extractedPaths => {
|
||||
// Success, set the prefs
|
||||
let now = Math.round(Date.now() / 1000);
|
||||
GMPPrefs.setInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, now, gmpAddon.id);
|
||||
// Remember our ABI, so that if the profile is migrated to another
|
||||
// platform or from 32 -> 64 bit, we notice and don't try to load the
|
||||
// unexecutable plugin library.
|
||||
let abi = GMPUtils._expectedABI(gmpAddon);
|
||||
log.info("Setting ABI to '" + abi + "' for " + gmpAddon.id);
|
||||
GMPPrefs.setString(GMPPrefs.KEY_PLUGIN_ABI, abi, gmpAddon.id);
|
||||
// Setting the version pref signals installation completion to consumers,
|
||||
// if you need to set other prefs etc. do it before this.
|
||||
GMPPrefs.setString(
|
||||
GMPPrefs.KEY_PLUGIN_VERSION,
|
||||
gmpAddon.version,
|
||||
gmpAddon.id
|
||||
);
|
||||
return extractedPaths;
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -309,10 +309,13 @@ function downloadLocalConfig() {
|
|||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @param options (optional)
|
||||
* @param options.httpsOnlyNoUpgrade
|
||||
* Prevents upgrade to https:// when HTTPS-Only Mode is enabled.
|
||||
* @return a promise that resolves to the path of a temporary file or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
function downloadFile(url) {
|
||||
function downloadFile(url, options = { httpsOnlyNoUpgrade: false }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.onload = function(response) {
|
||||
|
@ -352,6 +355,7 @@ function downloadFile(url) {
|
|||
xhr.responseType = "arraybuffer";
|
||||
try {
|
||||
xhr.open("GET", url);
|
||||
xhr.channel.loadInfo.httpsOnlyNoUpgrade = options.httpsOnlyNoUpgrade;
|
||||
// Use conservative TLS settings. See bug 1325501.
|
||||
// TODO move to ServiceRequest.
|
||||
if (xhr.channel instanceof Ci.nsIHttpChannelInternal) {
|
||||
|
@ -480,11 +484,14 @@ const ProductAddonChecker = {
|
|||
*
|
||||
* @param addon
|
||||
* The addon to download.
|
||||
* @param options (optional)
|
||||
* @param options.httpsOnlyNoUpgrade
|
||||
* Prevents upgrade to https:// when HTTPS-Only Mode is enabled.
|
||||
* @return a promise that resolves to the temporary file downloaded or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
async downloadAddon(addon) {
|
||||
let path = await downloadFile(addon.URL);
|
||||
async downloadAddon(addon, options = { httpsOnlyNoUpgrade: false }) {
|
||||
let path = await downloadFile(addon.URL, options);
|
||||
try {
|
||||
await verifyFile(addon, path);
|
||||
return path;
|
||||
|
|
Загрузка…
Ссылка в новой задаче