зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1614969 - Check download with MixedContentBlocker r=ckerschb
Differential Revision: https://phabricator.services.mozilla.com/D73302
This commit is contained in:
Родитель
147fdbb684
Коммит
5bb89780fa
|
@ -45,6 +45,9 @@ InsecurePasswordsPresentOnIframe=Password fields present on an insecure (http://
|
||||||
LoadingMixedActiveContent2=Loading mixed (insecure) active content “%1$S” on a secure page
|
LoadingMixedActiveContent2=Loading mixed (insecure) active content “%1$S” on a secure page
|
||||||
LoadingMixedDisplayContent2=Loading mixed (insecure) display content “%1$S” on a secure page
|
LoadingMixedDisplayContent2=Loading mixed (insecure) display content “%1$S” on a secure page
|
||||||
LoadingMixedDisplayObjectSubrequestDeprecation=Loading mixed (insecure) content “%1$S” within a plugin on a secure page is discouraged and will be blocked soon.
|
LoadingMixedDisplayObjectSubrequestDeprecation=Loading mixed (insecure) content “%1$S” within a plugin on a secure page is discouraged and will be blocked soon.
|
||||||
|
# LOCALIZATION NOTE: "%S" is the URI of the insecure mixed content download
|
||||||
|
MixedContentBlockedDownload = Blocked downloading insecure content “%S”.
|
||||||
|
|
||||||
# LOCALIZATION NOTE: Do not translate "allow-scripts", "allow-same-origin", "sandbox" or "iframe"
|
# LOCALIZATION NOTE: Do not translate "allow-scripts", "allow-same-origin", "sandbox" or "iframe"
|
||||||
BothAllowScriptsAndSameOriginPresent=An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can remove its sandboxing.
|
BothAllowScriptsAndSameOriginPresent=An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can remove its sandboxing.
|
||||||
# LOCALIZATION NOTE: Do not translate "allow-top-navigation-by-user-activation", "allow-top-navigation", "sandbox" or "iframe"
|
# LOCALIZATION NOTE: Do not translate "allow-top-navigation-by-user-activation", "allow-top-navigation", "sandbox" or "iframe"
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "mozilla/ExtensionPolicyService.h"
|
#include "mozilla/ExtensionPolicyService.h"
|
||||||
#include "mozilla/Logging.h"
|
#include "mozilla/Logging.h"
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
|
#include "LoadInfo.h"
|
||||||
#include "mozilla/StaticPrefs_extensions.h"
|
#include "mozilla/StaticPrefs_extensions.h"
|
||||||
#include "mozilla/StaticPrefs_dom.h"
|
#include "mozilla/StaticPrefs_dom.h"
|
||||||
|
|
||||||
|
@ -1039,3 +1040,77 @@ bool nsContentSecurityUtils::ValidateScriptFilename(const char* aFilename,
|
||||||
// builds and return false to prevent execution in non-debug builds.
|
// builds and return false to prevent execution in non-debug builds.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
void nsContentSecurityUtils::LogMessageToConsole(nsIHttpChannel* aChannel,
|
||||||
|
const char* aMsg) {
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t windowID = 0;
|
||||||
|
rv = aChannel->GetTopLevelContentWindowId(&windowID);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!windowID) {
|
||||||
|
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
||||||
|
loadInfo->GetInnerWindowID(&windowID);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoString localizedMsg;
|
||||||
|
nsAutoCString spec;
|
||||||
|
uri->GetSpec(spec);
|
||||||
|
AutoTArray<nsString, 1> params = {NS_ConvertUTF8toUTF16(spec)};
|
||||||
|
rv = nsContentUtils::FormatLocalizedString(
|
||||||
|
nsContentUtils::eSECURITY_PROPERTIES, aMsg, params, localizedMsg);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsContentUtils::ReportToConsoleByWindowID(
|
||||||
|
localizedMsg, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Security"),
|
||||||
|
windowID, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool nsContentSecurityUtils::IsDownloadAllowed(
|
||||||
|
nsIChannel* aChannel, const nsAutoCString& aMimeTypeGuess) {
|
||||||
|
MOZ_ASSERT(aChannel, "IsDownloadAllowed without channel?");
|
||||||
|
if (!StaticPrefs::dom_block_download_insecure()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
||||||
|
if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> contentLocation;
|
||||||
|
aChannel->GetURI(getter_AddRefs(contentLocation));
|
||||||
|
|
||||||
|
nsCOMPtr<nsIPrincipal> loadingPrincipal = loadInfo->GetLoadingPrincipal();
|
||||||
|
// Creating a fake Loadinfo that is just used for the MCB check.
|
||||||
|
nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
|
||||||
|
new LoadInfo(loadingPrincipal, loadInfo->TriggeringPrincipal(), nullptr,
|
||||||
|
nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
|
||||||
|
nsIContentPolicy::TYPE_OTHER);
|
||||||
|
|
||||||
|
int16_t decission = nsIContentPolicy::ACCEPT;
|
||||||
|
nsMixedContentBlocker::ShouldLoad(false, // aHadInsecureImageRedirect
|
||||||
|
contentLocation, // aContentLocation,
|
||||||
|
secCheckLoadInfo, // aLoadinfo
|
||||||
|
aMimeTypeGuess, // aMimeGuess,
|
||||||
|
&decission // aDecision
|
||||||
|
);
|
||||||
|
if (decission == nsIContentPolicy::ACCEPT) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
||||||
|
if (httpChannel) {
|
||||||
|
LogMessageToConsole(httpChannel, "MixedContentBlockedDownload");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -50,6 +50,12 @@ class nsContentSecurityUtils {
|
||||||
// If any of the two disallows framing, the channel will be cancelled.
|
// If any of the two disallows framing, the channel will be cancelled.
|
||||||
static void PerformCSPFrameAncestorAndXFOCheck(nsIChannel* aChannel);
|
static void PerformCSPFrameAncestorAndXFOCheck(nsIChannel* aChannel);
|
||||||
|
|
||||||
|
// Helper function to Check if a Download is allowed;
|
||||||
|
static bool IsDownloadAllowed(nsIChannel* aChannel,
|
||||||
|
const nsAutoCString& aMimeTypeGuess);
|
||||||
|
// Logs an Error Message to the Console
|
||||||
|
static void LogMessageToConsole(nsIHttpChannel* aChannel, const char* aMsg);
|
||||||
|
|
||||||
#if defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
static void AssertAboutPageHasCSP(mozilla::dom::Document* aDocument);
|
static void AssertAboutPageHasCSP(mozilla::dom::Document* aDocument);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
[DEFAULT]
|
||||||
|
support-files =
|
||||||
|
download_page.html
|
||||||
|
download_server.sjs
|
||||||
|
|
||||||
|
[browser_test_mixed_content_download.js]
|
|
@ -0,0 +1,98 @@
|
||||||
|
let INSECURE_BASE_URL =
|
||||||
|
getRootDirectory(gTestPath).replace(
|
||||||
|
"chrome://mochitests/content/",
|
||||||
|
"http://example.com/"
|
||||||
|
) + "download_page.html";
|
||||||
|
let SECURE_BASE_URL =
|
||||||
|
getRootDirectory(gTestPath).replace(
|
||||||
|
"chrome://mochitests/content/",
|
||||||
|
"https://example.com/"
|
||||||
|
) + "download_page.html";
|
||||||
|
|
||||||
|
function shouldPromptDownload() {
|
||||||
|
// Waits until the download Prompt is shown
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
Services.wm.addListener({
|
||||||
|
onOpenWindow(xulWin) {
|
||||||
|
Services.wm.removeListener(this);
|
||||||
|
let win = xulWin.docShell.domWindow;
|
||||||
|
waitForFocus(() => {
|
||||||
|
if (
|
||||||
|
win.location ==
|
||||||
|
"chrome://mozapps/content/downloads/unknownContentType.xhtml"
|
||||||
|
) {
|
||||||
|
resolve();
|
||||||
|
info("Trying to close window");
|
||||||
|
win.close();
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
|
}
|
||||||
|
}, win);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const CONSOLE_ERROR_MESSAGE = "was blocked because it was insecure.";
|
||||||
|
|
||||||
|
function shouldConsoleError() {
|
||||||
|
// Waits until CONSOLE_ERROR_MESSAGE was logged
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
function listener(msgObj) {
|
||||||
|
let text = msgObj.message;
|
||||||
|
if (text.includes(CONSOLE_ERROR_MESSAGE)) {
|
||||||
|
Services.console.unregisterListener(listener);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Services.console.registerListener(listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runTest(url, link, check, decscription) {
|
||||||
|
let tab = BrowserTestUtils.addTab(gBrowser, url);
|
||||||
|
gBrowser.selectedTab = tab;
|
||||||
|
|
||||||
|
let browser = gBrowser.getBrowserForTab(tab);
|
||||||
|
await BrowserTestUtils.browserLoaded(browser);
|
||||||
|
|
||||||
|
info("Checking: " + decscription);
|
||||||
|
|
||||||
|
let checkPromise = check();
|
||||||
|
// Click the Link to trigger the download
|
||||||
|
SpecialPowers.spawn(gBrowser.selectedBrowser, [link], contentLink => {
|
||||||
|
content.document.getElementById(contentLink).click();
|
||||||
|
});
|
||||||
|
|
||||||
|
await checkPromise;
|
||||||
|
|
||||||
|
ok(true, decscription);
|
||||||
|
BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await runTest(
|
||||||
|
INSECURE_BASE_URL,
|
||||||
|
"insecure",
|
||||||
|
shouldPromptDownload,
|
||||||
|
"Insecure -> Insecure should download"
|
||||||
|
);
|
||||||
|
await runTest(
|
||||||
|
INSECURE_BASE_URL,
|
||||||
|
"secure",
|
||||||
|
shouldPromptDownload,
|
||||||
|
"Insecure -> Secure should download"
|
||||||
|
);
|
||||||
|
await runTest(
|
||||||
|
SECURE_BASE_URL,
|
||||||
|
"insecure",
|
||||||
|
shouldConsoleError,
|
||||||
|
"Secure -> Insecure should Error"
|
||||||
|
);
|
||||||
|
await runTest(
|
||||||
|
SECURE_BASE_URL,
|
||||||
|
"secure",
|
||||||
|
shouldPromptDownload,
|
||||||
|
"Secure -> Secure should Download"
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=676619
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
|
||||||
|
<title>Test for the download attribute</title>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
hi
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const host = window.location.host;
|
||||||
|
const path = location.pathname.replace("download_page.html","download_server.sjs");
|
||||||
|
|
||||||
|
const secureLink = document.createElement("a");
|
||||||
|
secureLink.href=`https://${host}/${path}`;
|
||||||
|
secureLink.download="true";
|
||||||
|
secureLink.textContent="Secure Link";
|
||||||
|
|
||||||
|
secureLink.id="secure";
|
||||||
|
|
||||||
|
const insecureLink = document.createElement("a");
|
||||||
|
insecureLink.href=`http://${host}/${path}`;
|
||||||
|
insecureLink.download="true";
|
||||||
|
insecureLink.id="insecure";
|
||||||
|
insecureLink.textContent="Not secure Link";
|
||||||
|
|
||||||
|
document.body.append(secureLink);
|
||||||
|
document.body.append(insecureLink);
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
// force the Browser to Show a Download Prompt
|
||||||
|
|
||||||
|
function handleRequest(request, response)
|
||||||
|
{
|
||||||
|
response.setHeader("Cache-Control", "no-cache", false);
|
||||||
|
response.setHeader("Content-Disposition", "attachment");
|
||||||
|
response.setHeader("Content-Type", "application/octet-stream");
|
||||||
|
response.write('🙈🙊🐵🙊');
|
||||||
|
}
|
|
@ -34,4 +34,5 @@ BROWSER_CHROME_MANIFESTS += [
|
||||||
'csp/browser.ini',
|
'csp/browser.ini',
|
||||||
'general/browser.ini',
|
'general/browser.ini',
|
||||||
'https-only/browser.ini',
|
'https-only/browser.ini',
|
||||||
|
'mixedcontentblocker/browser.ini'
|
||||||
]
|
]
|
||||||
|
|
|
@ -1506,6 +1506,12 @@
|
||||||
value: true
|
value: true
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
# Block Insecure downloads from Secure Origins
|
||||||
|
- name: dom.block_download_insecure
|
||||||
|
type: bool
|
||||||
|
value: @IS_NIGHTLY_BUILD@
|
||||||
|
mirror: always
|
||||||
|
|
||||||
# Block multiple window.open() per single event.
|
# Block multiple window.open() per single event.
|
||||||
- name: dom.block_multiple_popups
|
- name: dom.block_multiple_popups
|
||||||
type: bool
|
type: bool
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
#include "nsIRedirectHistoryEntry.h"
|
#include "nsIRedirectHistoryEntry.h"
|
||||||
#include "nsOSHelperAppService.h"
|
#include "nsOSHelperAppService.h"
|
||||||
#include "nsOSHelperAppServiceChild.h"
|
#include "nsOSHelperAppServiceChild.h"
|
||||||
|
#include "nsContentSecurityUtils.h"
|
||||||
|
|
||||||
// used to access our datastore of user-configured helper applications
|
// used to access our datastore of user-configured helper applications
|
||||||
#include "nsIHandlerService.h"
|
#include "nsIHandlerService.h"
|
||||||
|
@ -1557,6 +1558,14 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||||
nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
|
nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
nsAutoCString MIMEType;
|
||||||
|
mMimeInfo->GetMIMEType(MIMEType);
|
||||||
|
|
||||||
|
if (!nsContentSecurityUtils::IsDownloadAllowed(aChannel, MIMEType)) {
|
||||||
|
mCanceled = true;
|
||||||
|
request->Cancel(NS_ERROR_ABORT);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIFileChannel> fileChan(do_QueryInterface(request));
|
nsCOMPtr<nsIFileChannel> fileChan(do_QueryInterface(request));
|
||||||
mIsFileChannel = fileChan != nullptr;
|
mIsFileChannel = fileChan != nullptr;
|
||||||
|
@ -1577,7 +1586,6 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||||
if (mBrowsingContext) {
|
if (mBrowsingContext) {
|
||||||
mMaybeCloseWindowHelper = new MaybeCloseWindowHelper(mBrowsingContext);
|
mMaybeCloseWindowHelper = new MaybeCloseWindowHelper(mBrowsingContext);
|
||||||
mMaybeCloseWindowHelper->SetShouldCloseWindow(mShouldCloseWindow);
|
mMaybeCloseWindowHelper->SetShouldCloseWindow(mShouldCloseWindow);
|
||||||
|
|
||||||
nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(request, &rv));
|
nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(request, &rv));
|
||||||
// Determine whether a new window was opened specifically for this request
|
// Determine whether a new window was opened specifically for this request
|
||||||
if (props) {
|
if (props) {
|
||||||
|
@ -1666,8 +1674,6 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest* request) {
|
||||||
|
|
||||||
bool alwaysAsk = true;
|
bool alwaysAsk = true;
|
||||||
mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
|
mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
|
||||||
nsAutoCString MIMEType;
|
|
||||||
mMimeInfo->GetMIMEType(MIMEType);
|
|
||||||
if (alwaysAsk) {
|
if (alwaysAsk) {
|
||||||
// But we *don't* ask if this mimeInfo didn't come from
|
// But we *don't* ask if this mimeInfo didn't come from
|
||||||
// our user configuration datastore and the user has said
|
// our user configuration datastore and the user has said
|
||||||
|
|
Загрузка…
Ссылка в новой задаче