Bug 966134: merge warning dialog should mention new email, not old email, and we should avoid storing the plaintext of signed-in users forever, r=markh
authorGavin Sharp <gavin@gavinsharp.com>
Thu, 30 Jan 2014 19:05:24 -0800
changeset 182248 d70e66df236348f66e9b24f234848de040fddf96
parent 182247 260266bdf0cace14ed04197e9417405281bafd43
child 182249 3cca5a116a0d9408328b6af182c84630affc45ef
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh
bugs966134
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 966134: merge warning dialog should mention new email, not old email, and we should avoid storing the plaintext of signed-in users forever, r=markh
browser/base/content/aboutaccounts/aboutaccounts.js
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -4,53 +4,68 @@
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FxAccounts.jsm");
 
-const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUser";
+const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
 const PREF_SYNC_SHOW_CUSTOMIZATION = "services.sync.ui.showCustomizationDialog";
 
 function log(msg) {
   //dump("FXA: " + msg + "\n");
 };
 
 function error(msg) {
   console.log("Firefox Account Error: " + msg + "\n");
 };
 
-function getPreviousAccountName() {
+function getPreviousAccountNameHash() {
   try {
     return Services.prefs.getComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString).data;
   } catch (_) {
     return "";
   }
 }
 
-function setPreviousAccountName(acctName) {
+function setPreviousAccountNameHash(acctName) {
   let string = Cc["@mozilla.org/supports-string;1"]
                .createInstance(Ci.nsISupportsString);
-  string.data = acctName;
+  string.data = sha256(acctName);
   Services.prefs.setComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString, string);
 }
 
-function needRelinkWarning(accountData) {
-  let prevAcct = getPreviousAccountName();
-  return prevAcct && prevAcct != accountData.email;
+function needRelinkWarning(acctName) {
+  let prevAcctHash = getPreviousAccountNameHash();
+  return prevAcctHash && prevAcctHash != sha256(acctName);
 }
 
-function promptForRelink() {
+// Given a string, returns the SHA265 hash in base64
+function sha256(str) {
+  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
+                    .createInstance(Ci.nsIScriptableUnicodeConverter);
+  converter.charset = "UTF-8";
+  // Data is an array of bytes.
+  let data = converter.convertToByteArray(str, {});
+  let hasher = Cc["@mozilla.org/security/hash;1"]
+                 .createInstance(Ci.nsICryptoHash);
+  hasher.init(hasher.SHA256);
+  hasher.update(data, data.length);
+
+  return hasher.finish(true);
+}
+
+function promptForRelink(acctName) {
   let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
   let continueLabel = sb.GetStringFromName("continue.label");
   let title = sb.GetStringFromName("relink.verify.title");
   let description = sb.formatStringFromName("relink.verify.description",
-                                            [Services.prefs.getCharPref(PREF_LAST_FXA_USER)], 1);
+                                            [acctName], 1);
   let body = sb.GetStringFromName("relink.verify.heading") +
              "\n\n" + description;
   let ps = Services.prompt;
   let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
                     (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
                     ps.BUTTON_POS_1_DEFAULT;
   let pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
                                      continueLabel, null, null, null,
@@ -109,27 +124,28 @@ let wrapper = {
       delete accountData.customizeSync;
     }
 
     // If the last fxa account used for sync isn't this account, we display
     // a modal dialog checking they really really want to do this...
     // (This is sync-specific, so ideally would be in sync's identity module,
     // but it's a little more seamless to do here, and sync is currently the
     // only fxa consumer, so...
-    if (needRelinkWarning(accountData) && !promptForRelink()) {
+    let newAccountEmail = accountData.email;
+    if (needRelinkWarning(newAccountEmail) && !promptForRelink(newAccountEmail)) {
       // we need to tell the page we successfully received the message, but
       // then bail without telling fxAccounts
       this.injectData("message", { status: "login" });
       // and reload the page or else it remains in a "signed in" state.
       window.location.reload();
       return;
     }
 
     // Remember who it was so we can log out next time.
-    setPreviousAccountName(accountData.email);
+    setPreviousAccountNameHash(newAccountEmail);
 
     fxAccounts.setSignedInUser(accountData).then(
       () => {
         this.injectData("message", { status: "login" });
         // until we sort out a better UX, just leave the jelly page in place.
         // If the account email is not yet verified, it will tell the user to
         // go check their email, but then it will *not* change state after
         // the verification completes (the browser will begin syncing, but