1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/passwordmgr/InsecurePasswordUtils.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,146 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +this.EXPORTED_SYMBOLS = [ "InsecurePasswordUtils" ]; 1.9 + 1.10 +const Ci = Components.interfaces; 1.11 +const Cu = Components.utils; 1.12 +const Cc = Components.classes; 1.13 + 1.14 +Cu.import("resource://gre/modules/Services.jsm"); 1.15 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.16 + 1.17 +XPCOMUtils.defineLazyModuleGetter(this, "devtools", 1.18 + "resource://gre/modules/devtools/Loader.jsm"); 1.19 + 1.20 +Object.defineProperty(this, "WebConsoleUtils", { 1.21 + get: function() { 1.22 + return devtools.require("devtools/toolkit/webconsole/utils").Utils; 1.23 + }, 1.24 + configurable: true, 1.25 + enumerable: true 1.26 +}); 1.27 + 1.28 +const STRINGS_URI = "chrome://global/locale/security/security.properties"; 1.29 +let l10n = new WebConsoleUtils.l10n(STRINGS_URI); 1.30 + 1.31 +this.InsecurePasswordUtils = { 1.32 + 1.33 + _sendWebConsoleMessage : function (messageTag, domDoc) { 1.34 + /* 1.35 + * All web console messages are warnings for now so I decided to set the 1.36 + * flag here and save a bit of the flag creation in the callers. 1.37 + * It's easy to expose this later if needed 1.38 + */ 1.39 + 1.40 + let windowId = WebConsoleUtils.getInnerWindowId(domDoc.defaultView); 1.41 + let category = "Insecure Password Field"; 1.42 + let flag = Ci.nsIScriptError.warningFlag; 1.43 + let message = l10n.getStr(messageTag); 1.44 + let consoleMsg = Cc["@mozilla.org/scripterror;1"] 1.45 + .createInstance(Ci.nsIScriptError); 1.46 + 1.47 + consoleMsg.initWithWindowID( 1.48 + message, "", 0, 0, 0, flag, category, windowId); 1.49 + 1.50 + Services.console.logMessage(consoleMsg); 1.51 + }, 1.52 + 1.53 + /* 1.54 + * Checks whether the passed uri is secure 1.55 + * Check Protocol Flags to determine if scheme is secure: 1.56 + * URI_DOES_NOT_RETURN_DATA - e.g. 1.57 + * "mailto" 1.58 + * URI_IS_LOCAL_RESOURCE - e.g. 1.59 + * "data", 1.60 + * "resource", 1.61 + * "moz-icon" 1.62 + * URI_INHERITS_SECURITY_CONTEXT - e.g. 1.63 + * "javascript" 1.64 + * URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g. 1.65 + * "https", 1.66 + * "moz-safe-about" 1.67 + * 1.68 + * The use of this logic comes directly from nsMixedContentBlocker.cpp 1.69 + * At the time it was decided to include these protocols since a secure 1.70 + * uri for mixed content blocker means that the resource can't be 1.71 + * easily tampered with because 1) it is sent over an encrypted channel or 1.72 + * 2) it is a local resource that never hits the network 1.73 + * or 3) it is a request sent without any response that could alter 1.74 + * the behavior of the page. It was decided to include the same logic 1.75 + * here both to be consistent with MCB and to make sure we cover all 1.76 + * "safe" protocols. Eventually, the code here and the code in MCB 1.77 + * will be moved to a common location that will be referenced from 1.78 + * both places. Look at 1.79 + * https://bugzilla.mozilla.org/show_bug.cgi?id=899099 for more info. 1.80 + */ 1.81 + _checkIfURIisSecure : function(uri) { 1.82 + let isSafe = false; 1.83 + let netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil); 1.84 + let ph = Ci.nsIProtocolHandler; 1.85 + 1.86 + if (netutil.URIChainHasFlags(uri, ph.URI_IS_LOCAL_RESOURCE) || 1.87 + netutil.URIChainHasFlags(uri, ph.URI_DOES_NOT_RETURN_DATA) || 1.88 + netutil.URIChainHasFlags(uri, ph.URI_INHERITS_SECURITY_CONTEXT) || 1.89 + netutil.URIChainHasFlags(uri, ph.URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT)) { 1.90 + 1.91 + isSafe = true; 1.92 + } 1.93 + 1.94 + return isSafe; 1.95 + }, 1.96 + 1.97 + /* 1.98 + * Checks whether the passed nested document is insecure 1.99 + * or is inside an insecure parent document. 1.100 + * 1.101 + * We check the chain of frame ancestors all the way until the top document 1.102 + * because MITM attackers could replace https:// iframes if they are nested inside 1.103 + * http:// documents with their own content, thus creating a security risk 1.104 + * and potentially stealing user data. Under such scenario, a user might not 1.105 + * get a Mixed Content Blocker message, if the main document is served over HTTP 1.106 + * and framing an HTTPS page as it would under the reverse scenario (http 1.107 + * inside https). 1.108 + */ 1.109 + _checkForInsecureNestedDocuments : function(domDoc) { 1.110 + let uri = domDoc.documentURIObject; 1.111 + if (domDoc.defaultView == domDoc.defaultView.parent) { 1.112 + // We are at the top, nothing to check here 1.113 + return false; 1.114 + } 1.115 + if (!this._checkIfURIisSecure(uri)) { 1.116 + // We are insecure 1.117 + return true; 1.118 + } 1.119 + // I am secure, but check my parent 1.120 + return this._checkForInsecureNestedDocuments(domDoc.defaultView.parent.document); 1.121 + }, 1.122 + 1.123 + 1.124 + /* 1.125 + * Checks if there are insecure password fields present on the form's document 1.126 + * i.e. passwords inside forms with http action, inside iframes with http src, 1.127 + * or on insecure web pages. If insecure password fields are present, 1.128 + * a log message is sent to the web console to warn developers. 1.129 + */ 1.130 + checkForInsecurePasswords : function (aForm) { 1.131 + var domDoc = aForm.ownerDocument; 1.132 + let pageURI = domDoc.defaultView.top.document.documentURIObject; 1.133 + let isSafePage = this._checkIfURIisSecure(pageURI); 1.134 + 1.135 + if (!isSafePage) { 1.136 + this._sendWebConsoleMessage("InsecurePasswordsPresentOnPage", domDoc); 1.137 + } 1.138 + 1.139 + // Check if we are on an iframe with insecure src, or inside another 1.140 + // insecure iframe or document. 1.141 + if (this._checkForInsecureNestedDocuments(domDoc)) { 1.142 + this._sendWebConsoleMessage("InsecurePasswordsPresentOnIframe", domDoc); 1.143 + } 1.144 + 1.145 + if (aForm.action.match(/^http:\/\//)) { 1.146 + this._sendWebConsoleMessage("InsecureFormActionPasswordsPresent", domDoc); 1.147 + } 1.148 + }, 1.149 +};