зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1059202 - CSP and CA verfication for Trusted Hosted Apps r=fabrice,sicking,rlb
This commit is contained in:
Родитель
1d9c2eb85b
Коммит
1c124be1f8
|
@ -246,6 +246,15 @@ pref("security.alternate_certificate_error_page", "certerror");
|
|||
|
||||
pref("security.warn_viewing_mixed", false); // Warning is disabled. See Bug 616712.
|
||||
|
||||
// 2 = strict certificate pinning checks.
|
||||
// This default preference is more strict than Firefox because B2G
|
||||
// currently does not have a way to install local root certificates.
|
||||
// Strict checking is effectively equivalent to non-strict checking as
|
||||
// long as that is true. If an ability to add local certificates is
|
||||
// added, there may be a need to change this pref.
|
||||
pref("security.cert_pinning.enforcement_level", 2);
|
||||
|
||||
|
||||
// Override some named colors to avoid inverse OS themes
|
||||
pref("ui.-moz-dialog", "#efebe7");
|
||||
pref("ui.-moz-dialogtext", "#101010");
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/* 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/. */
|
||||
|
||||
/* global Components, Services, dump */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["TrustedHostedAppsUtils"];
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// On Android, define the "debug" function as a binding of the "d" function
|
||||
// from the AndroidLog module so it gets the "debug" priority and a log tag.
|
||||
// We always report debug messages on Android because it's unnecessary
|
||||
// to restrict reporting, per bug 1003469.
|
||||
let debug = Cu
|
||||
.import("resource://gre/modules/AndroidLog.jsm", {})
|
||||
.AndroidLog.d.bind(null, "TrustedHostedAppsUtils");
|
||||
#else
|
||||
// Elsewhere, report debug messages only if dom.mozApps.debug is set to true.
|
||||
// The pref is only checked once, on startup, so restart after changing it.
|
||||
let debug = Services.prefs.getBoolPref("dom.mozApps.debug") ?
|
||||
aMsg => dump("-*- TrustedHostedAppsUtils.jsm : " + aMsg + "\n") :
|
||||
() => {};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Verification functions for Trusted Hosted Apps.
|
||||
* (Manifest signature verification is in Webapps.jsm as part of
|
||||
* regular signature verification.)
|
||||
*/
|
||||
this.TrustedHostedAppsUtils = {
|
||||
|
||||
/**
|
||||
* Check if the given host is pinned in the CA pinning database.
|
||||
*/
|
||||
isHostPinned: function (aUrl) {
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(aUrl, null, null);
|
||||
} catch(e) {
|
||||
debug("Host parsing failed: " + e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: use nsSiteSecurityService.isSecureURI()
|
||||
if (!uri.host || "https" != uri.scheme) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check certificate pinning
|
||||
let siteSecurityService;
|
||||
try {
|
||||
siteSecurityService = Cc["@mozilla.org/ssservice;1"]
|
||||
.getService(Ci.nsISiteSecurityService);
|
||||
} catch (e) {
|
||||
debug("nsISiteSecurityService error: " + e);
|
||||
// unrecoverable error, don't bug the user
|
||||
throw "CERTDB_ERROR";
|
||||
}
|
||||
|
||||
if (siteSecurityService.isSecureHost(Ci.nsISiteSecurityService.HEADER_HPKP, uri.host, 0)) {
|
||||
debug("\tvalid certificate pinning for host: " + uri.host + "\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
debug("\tHost NOT pinned: " + uri.host + "\n");
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Take a CSP policy string as input and ensure that it contains at
|
||||
* least the directives that are required ('script-src' and
|
||||
* 'style-src'). If the CSP policy string is 'undefined' or does
|
||||
* not contain some of the required csp directives the function will
|
||||
* return empty list with status set to false. Otherwise a parsed
|
||||
* list of the unique sources listed from the required csp
|
||||
* directives is returned.
|
||||
*/
|
||||
getCSPWhiteList: function(aCsp) {
|
||||
let isValid = false;
|
||||
let whiteList = [];
|
||||
let requiredDirectives = [ "script-src", "style-src" ];
|
||||
|
||||
if (aCsp) {
|
||||
let validDirectives = [];
|
||||
let directives = aCsp.split(";");
|
||||
// TODO: Use nsIContentSecurityPolicy
|
||||
directives
|
||||
.map(aDirective => aDirective.trim().split(" "))
|
||||
.filter(aList => aList.length > 1)
|
||||
// we only restrict on requiredDirectives
|
||||
.filter(aList => (requiredDirectives.indexOf(aList[0]) != -1))
|
||||
.forEach(aList => {
|
||||
// aList[0] contains the directive name.
|
||||
// aList[1..n] contains sources.
|
||||
let directiveName = aList.shift()
|
||||
let sources = aList;
|
||||
|
||||
if ((-1 == validDirectives.indexOf(directiveName))) {
|
||||
validDirectives.push(directiveName);
|
||||
}
|
||||
whiteList.push(...sources.filter(
|
||||
// 'self' is checked separately during manifest check
|
||||
aSource => (aSource !="'self'" && whiteList.indexOf(aSource) == -1)
|
||||
));
|
||||
});
|
||||
|
||||
// Check if all required directives are present.
|
||||
isValid = requiredDirectives.length === validDirectives.length;
|
||||
|
||||
if (!isValid) {
|
||||
debug("White list doesn't contain all required directives!");
|
||||
whiteList = [];
|
||||
}
|
||||
}
|
||||
|
||||
debug("White list contains " + whiteList.length + " hosts");
|
||||
return { list: whiteList, valid: isValid };
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify that the given csp is valid:
|
||||
* 1. contains required directives "script-src" and "style-src"
|
||||
* 2. required directives contain only "https" URLs
|
||||
* 3. domains of the restricted sources exist in the CA pinning database
|
||||
*/
|
||||
verifyCSPWhiteList: function(aCsp) {
|
||||
let domainWhitelist = this.getCSPWhiteList(aCsp);
|
||||
if (!domainWhitelist.valid) {
|
||||
debug("TRUSTED_APPLICATION_WHITELIST_PARSING_FAILED");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!domainWhitelist.list.every(aUrl => this.isHostPinned(aUrl))) {
|
||||
debug("TRUSTED_APPLICATION_WHITELIST_VALIDATION_FAILED");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
|
@ -64,6 +64,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "ScriptPreloader",
|
||||
"resource://gre/modules/ScriptPreloader.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TrustedHostedAppsUtils",
|
||||
"resource://gre/modules/TrustedHostedAppsUtils.jsm");
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
XPCOMUtils.defineLazyGetter(this, "libcutils", function() {
|
||||
Cu.import("resource://gre/modules/systemlibs.js");
|
||||
|
@ -1411,6 +1414,24 @@ this.DOMApplicationRegistry = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if launching trusted hosted app
|
||||
if (this.kTrustedHosted == app.kind) {
|
||||
debug("Launching Trusted Hosted App!");
|
||||
// sanity check on manifest host's CA
|
||||
// (proper CA check with pinning is done by regular networking code)
|
||||
if (!TrustedHostedAppsUtils.isHostPinned(aManifestURL)) {
|
||||
debug("Trusted App Host certificate Not OK");
|
||||
aOnFailure("TRUSTED_APPLICATION_HOST_CERTIFICATE_INVALID");
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Trusted App Host pins exist");
|
||||
if (!TrustedHostedAppsUtils.verifyCSPWhiteList(app.csp)) {
|
||||
aOnFailure("TRUSTED_APPLICATION_WHITELIST_VALIDATION_FAILED");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We have to clone the app object as nsIDOMApplication objects are
|
||||
// stringified as an empty object. (see bug 830376)
|
||||
let appClone = AppsUtils.cloneAppObject(app);
|
||||
|
@ -2295,6 +2316,23 @@ this.DOMApplicationRegistry = {
|
|||
// in which case we don't need to load it.
|
||||
if (app.manifest) {
|
||||
if (checkManifest()) {
|
||||
if (this.kTrustedHosted == this.appKind(app, app.manifest)) {
|
||||
// sanity check on manifest host's CA
|
||||
// (proper CA check with pinning is done by regular networking code)
|
||||
if (!TrustedHostedAppsUtils.isHostPinned(app.manifestURL)) {
|
||||
sendError("TRUSTED_APPLICATION_HOST_CERTIFICATE_INVALID");
|
||||
return;
|
||||
}
|
||||
|
||||
// Signature of the manifest should be verified here.
|
||||
// Bug 1059216.
|
||||
|
||||
if (!TrustedHostedAppsUtils.verifyCSPWhiteList(app.manifest.csp)) {
|
||||
sendError("TRUSTED_APPLICATION_WHITELIST_VALIDATION_FAILED");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
installApp();
|
||||
}
|
||||
return;
|
||||
|
@ -2319,6 +2357,19 @@ this.DOMApplicationRegistry = {
|
|||
app.manifest = xhr.response;
|
||||
if (checkManifest()) {
|
||||
app.etag = xhr.getResponseHeader("Etag");
|
||||
if (this.kTrustedHosted == this.appKind(app, app.manifest)) {
|
||||
// checking trusted host for pinning is not needed here, since
|
||||
// network code will have already done that
|
||||
|
||||
// Signature of the manifest should be verified here.
|
||||
// Bug 1059216.
|
||||
|
||||
if (!TrustedHostedAppsUtils.verifyCSPWhiteList(app.manifest.csp)) {
|
||||
sendError("TRUSTED_APPLICATION_WHITELIST_VALIDATION_FAILED");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
installApp();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -44,6 +44,7 @@ EXTRA_PP_JS_MODULES += [
|
|||
'AppsUtils.jsm',
|
||||
'OperatorApps.jsm',
|
||||
'ScriptPreloader.jsm',
|
||||
'TrustedHostedAppsUtils.jsm',
|
||||
'Webapps.jsm',
|
||||
]
|
||||
|
||||
|
|
|
@ -1729,7 +1729,7 @@ pref("security.mixed_content.block_active_content", false);
|
|||
pref("security.mixed_content.block_display_content", false);
|
||||
|
||||
// Disable pinning checks by default.
|
||||
pref("security.cert_pinning.enforcement_level", 0);
|
||||
pref("security.cert_pinning.enforcement_level", 2);
|
||||
|
||||
// Modifier key prefs: default to Windows settings,
|
||||
// menu access key = alt, accelerator key = control.
|
||||
|
@ -4093,7 +4093,7 @@ pref("dom.mozApps.signed_apps_installable_from", "https://marketplace.firefox.co
|
|||
// Only checked on startup, so restart after changing this pref.
|
||||
// Ignored on Android, where we always report debug messages because it's
|
||||
// unnecessary to restrict reporting, per bug 1003469.
|
||||
pref("dom.mozApps.debug", false);
|
||||
pref("dom.mozApps.debug", true);
|
||||
|
||||
// Minimum delay in milliseconds between network activity notifications (0 means
|
||||
// no notifications). The delay is the same for both download and upload, though
|
||||
|
|
Загрузка…
Ссылка в новой задаче