Bug 1337246 - Part 2 - Detect local IP addresses to ignore for insecure password warnings. r=MattN draft
authorJohann Hofmann <jhofmann@mozilla.com>
Mon, 08 May 2017 14:58:53 -0400
changeset 574343 db9a82f764fddbd9cbd8fd625fa8e4caf6b77577
parent 574335 081b160d2822487a2e3b4e05ba34d614f0ad5042
child 574344 7ccb47feae6e60f979b560d6c4f2e8c0fce4d3d6
push id57674
push userbmo:jhofmann@mozilla.com
push dateMon, 08 May 2017 18:59:31 +0000
reviewersMattN
bugs1337246
milestone55.0a1
Bug 1337246 - Part 2 - Detect local IP addresses to ignore for insecure password warnings. r=MattN MozReview-Commit-ID: 2IWvOJNTnNB
modules/libpref/init/all.js
toolkit/components/passwordmgr/InsecurePasswordUtils.jsm
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2386,16 +2386,17 @@ pref("security.block_script_with_wrong_m
 // Block images of wrong MIME for XCTO: nosniff.
 pref("security.xcto_nosniff_block_images", false);
 
 // OCSP must-staple
 pref("security.ssl.enable_ocsp_must_staple", true);
 
 // Insecure Form Field Warning
 pref("security.insecure_field_warning.contextual.enabled", false);
+pref("security.insecure_field_warning.ignore_local_ip_address", true);
 
 // Disable pinning checks by default.
 pref("security.cert_pinning.enforcement_level", 0);
 // Do not process hpkp headers rooted by not built in roots by default.
 // This is to prevent accidental pinning from MITM devices and is used
 // for tests.
 pref("security.cert_pinning.process_headers_from_non_builtin_roots", false);
 
--- a/toolkit/components/passwordmgr/InsecurePasswordUtils.jsm
+++ b/toolkit/components/passwordmgr/InsecurePasswordUtils.jsm
@@ -1,26 +1,35 @@
 /* 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/. */
 
+/* ownerGlobal doesn't exist in content privileged windows. */
+/* eslint-disable mozilla/use-ownerGlobal */
+
 this.EXPORTED_SYMBOLS = [ "InsecurePasswordUtils" ];
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 const STRINGS_URI = "chrome://global/locale/security/security.properties";
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gContentSecurityManager",
                                    "@mozilla.org/contentsecuritymanager;1",
                                    "nsIContentSecurityManager");
 XPCOMUtils.defineLazyServiceGetter(this, "gScriptSecurityManager",
                                    "@mozilla.org/scriptsecuritymanager;1",
                                    "nsIScriptSecurityManager");
+XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
+                                  "resource://gre/modules/LoginHelper.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "log", () => {
+  return LoginHelper.createLogger("InsecurePasswordUtils");
+});
 
 /*
  * A module that provides utility functions for form security.
  *
  * Note:
  *  This module uses isSecureContextIfOpenerIgnored instead of isSecureContext.
  *
  *  We don't want to expose JavaScript APIs in a non-Secure Context even if
@@ -84,29 +93,57 @@ this.InsecurePasswordUtils = {
       } else {
         isFormSubmitSecure = true;
       }
     }
 
     return { isFormSubmitHTTP, isFormSubmitSecure };
   },
 
+  _isPrincipalForLocalIPAddress(aPrincipal) {
+    try {
+      let uri = aPrincipal.URI;
+      if (Services.io.hostnameIsLocalIPAddress(uri)) {
+        log.debug("hasInsecureLoginForms: detected local IP address:", uri);
+        return true;
+      }
+    } catch (e) {
+      log.debug("hasInsecureLoginForms: unable to check for local IP address:", e);
+    }
+    return false;
+  },
+
   /**
    * Checks if there are insecure password fields present on the form's document
    * i.e. passwords inside forms with http action, inside iframes with http src,
    * or on insecure web pages.
    *
    * @param {FormLike} aForm A form-like object. @See {LoginFormFactory}
    * @return {boolean} whether the form is secure
    */
   isFormSecure(aForm) {
     // Ignores window.opener, see top level documentation.
-    // ownerGlobal doesn't exist in content privileged windows.
-    // eslint-disable-next-line mozilla/use-ownerGlobal
     let isSafePage = aForm.ownerDocument.defaultView.isSecureContextIfOpenerIgnored;
+
+    // Ignore insecure documents with URLs that are local IP addresses.
+    // This is done because the vast majority of routers and other devices
+    // on the network do not use HTTPS, making this warning show up almost
+    // constantly on local connections, which annoys users and hurts our cause.
+    if (!isSafePage && this._ignoreLocalIPAddress) {
+      let isLocalIP = this._isPrincipalForLocalIPAddress(aForm.rootElement.nodePrincipal);
+      let topWindow = aForm.ownerDocument.defaultView.top;
+      let topIsLocalIP = this._isPrincipalForLocalIPAddress(topWindow.document.nodePrincipal);
+
+      // Only consider the page safe if the top window has a local IP address
+      // and, if this is an iframe, the iframe also has a local IP address.
+      if (isLocalIP && topIsLocalIP) {
+        isSafePage = true;
+      }
+    }
+
     let { isFormSubmitSecure, isFormSubmitHTTP } = this._checkFormSecurity(aForm);
 
     return isSafePage && (isFormSubmitSecure || !isFormSubmitHTTP);
   },
 
   /**
    * Report insecure password fields in a form to the web console to warn developers.
    *
@@ -152,8 +189,11 @@ this.InsecurePasswordUtils = {
       passwordSafety = 4;
     } else {
       passwordSafety = 5;
     }
 
     Services.telemetry.getHistogramById("PWMGR_LOGIN_PAGE_SAFETY").add(passwordSafety);
   },
 };
+
+XPCOMUtils.defineLazyPreferenceGetter(this.InsecurePasswordUtils, "_ignoreLocalIPAddress",
+                                      "security.insecure_field_warning.ignore_local_ip_address", true);