Importing SHA hashed password into Firebase and Identity Platform

2020-02-17

or…how i learned that firebase imports passwords as hash(salt+password)…the really hard way.

Last week i assisted a customer in getting ready to import their existing users into Google Identity Platform. As is the case with many companies, their userbase’s identity store was your basic run of the mill salted password store. This is usually a no-brainer to migrate and the identity platform, firebase docs cover that ad nauseam (see Migrating users from an existing app

What stood apart in with this customer was the choice of they had i their existing system: SHA-1. No big deal i thought, the docs cover it.

However, the story more complicated quickly: a known password for a test user just didn’t work…the customer knew the original password, the salt used and given those two the password hash to import:

email = dev@foooo.com
raw_password = Password1
password_hash= kfjS+dtoePcY0vEKx1w5ol2ZJ88=
raw_salt = XiwiihQ=

I looked at this for hours and then noticed how they seeded their users in their users store:

sha1(password+salt)

the salt value was appended to the password and those two together got hashed…that left me thinking of the scheme firebase/cloud identity uses..I then looked around the firebase SDK for anyplace where you set/define the order since it may well be defaulting to hash(password+salt)..

It turns the ability to set the order is not surfaced in the SDK but is there in the firebase cli as the --hash-input-order= switch here deep in the code.

 "--hash-input-order <hashInputOrder>",
    "specify the order of password and salt. Possible values are SALT_FIRST and PASSWORD_FIRST. " +

So then i went ahead to test this. I had to setup the firebase CLI, create an import file using the hash i was given and the b64 encoded salt

  {
    "users": [
      {
        "localId": "dev@foooo.com",
        "email": "dev@foooo.com",
        "passwordHash": "kfjS+dtoePcY0vEKx1w5ol2ZJ88=",
        "salt": "WGl3aWloUT0=",
        "displayName": "dev@foooo.com"
      }
    ]
  }

in python, the values derived from the original password and hash woud’ve been:

raw_password = 'Password1'
raw_salt = 'XiwiihQ='

print hashlib.sha1(  raw_password + raw_salt ).hexdigest().decode("hex").encode("base64")
print raw_salt.encode("base64")

Then ran the import using the cli:

$ firebase auth:import user.json --hash-algo=SHA1 --rounds=1 --hash-input-order=PASSWORD_FIRST
Processing user.json (239 bytes)
Starting importing 1 account(s).
✔  Imported successfully.

At that point, i used the sample firebase “login client” below to test..take my word for it…success!

Now..your next question is “why can’t i use an sdk client?”…well, i don’t know, i’ll file a FR to the git repo to add it in…but in a quick look at the code, it just looks like you’d need to add in the following parameter to each user’s entry:

  • passwordHashOrder: "SALT_AND_PASSWORD" , or "PASSWORD_AND_SALT"

thats all…i spent maybe 7 hours on this one little thing…hope this helps someone out.


login.js:

The following is a sample firebase ’login’ client which will take an email/password and attempt a login.

var firebase = require("firebase/app");
require("firebase/auth");


const email = "dev@fooooo.com";
const password = "Password1";

var firebaseConfig = {
    apiKey: "your_api_key",
    authDomain: "yourproject.firebaseapp.com",
    databaseURL: "https://yourproject.firebaseio.com",
    projectId: "yourproject",
    appId: "yourproject",
  };

firebase.initializeApp(firebaseConfig);

firebase.auth().signInWithEmailAndPassword(email, password).then(result => {
  console.log("logged in as: " + result.user.email);
}).catch(function(error) {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    console.log(errorMessage);
  });

import.js:

Just for reference, here is the sample admin client you’d normally use to import…this won’t work since the sdk libraries don’t support the hash order but i’m adding it in just for reference….It will work if you toggle the password scheme back to the defaults. i.,e use shasum.update(raw_password + raw_salt); in the snippet below

Sample app to import users to firebase. Will always use salt+password

var admin = require('firebase-admin');

//export GOOGLE_APPLICATION_CREDENTIALS=/path/to/svc_account.json

admin.initializeApp({
    credential: admin.credential.applicationDefault(),
});
  
const email = "dev@foooo.com";
const raw_password = 'Password1'
const raw_salt = 'XiwiihQ='

const crypto = require('crypto')
  , shasum = crypto.createHash('sha1');
//shasum.update(raw_salt + raw_password);
shasum.update(raw_password + raw_salt);
const password_hash = shasum.digest()

console.log("SHA1: " +  Buffer.from(password_hash).toString('base64'))
console.log("Salt (b64): " + Buffer.from(raw_salt).toString('base64'));

admin.auth().importUsers([{
    uid: email,
    email: email,
    passwordHash: Buffer.from(password_hash),
    passwordSalt: Buffer.from(raw_salt)
  }], {
    hash: {
      algorithm: 'SHA1',
      rounds: 1
    }
  })
    .then(function(results) {
      console.log(results);
      return process.exit(0);
    })
    .catch(function(error) {
      console.log('Error importing users:', error);
      return process.exit(1);
    });

This site supports webmentions. Send me a mention via this form.



comments powered by Disqus