halesec

Cloud. Security. Dogs.
Not necessarily in that order.
PGP: 0xC0EE29073A219F19

View My GitHub Profile

2018-10-08 - 5 min read.

Securely managing 2FA secrets

by James Hale

As much as I can, I tend to MFA all the things. Even if the account isn’t particularly sensitive, if it supports MFA and my name or any personal details are attached to it, it gets MFA enabled. This leaves me with a lot of 2FA shared secrets that need to be stored securely.

I, like most, started out by using Google Authenticator to generate 2FA codes. It served the purpose, but there was one time where my phone got reset and I lost all of my 2FA codes. That sucked. There is no native way to back up or export shared secrets from Google Authenticator, so there wasn’t an obvious mitigation strategy to be had.

Clearly, I needed a more resilient way to do this. I’ve iterated through a couple of solutions, and this is what I’ve landed on.

Not looking to get burned again, I started to use Authy for my 2FA storage. Authy lets you sync 2FA codes between devices, so it would ensure that I could still get into my accounts, should my phone get trashed or stolen. I used Authy for a while and even recommended it to others.

Although I liked the problem that they solved, the fact that it was browser-based, really bugged me and I never got over it. I also have a general distrust of cloud-syncing services that hold really sensitive data, unless I have a pretty solid understanding of the encryption being used, or I control it myself (I hold the keys).

Also, Authy being browser-based add another exfil vector if my browser has something nefarious installed.

I wanted to switch back to Google Authenticator, but I didn’t want to have to re-issue all of my 2FA shared secrets. Aside from being a general pain (we’re talking like 30+), it was prone to errors and if I rushed through one, I risked locking myself out of that account.

So, I looked for a way to extract my Authy shared secrets so that I could re-enroll them in Authenticator, but having learned my lesson from last time, also back them up in a offline KeePass database.

I found a GitHub Gist thread that talks about using the Chrome console to pull the secrets from a decrypted Authy Chrome app. Authy to 1Password · GitHub

The comment that is linked provides a really elegant way to have Authy dump those to the Chrome console.

Although, one thing to note: This method does not work for “Authy-native” 2FA accounts. I have two of which, Coinbase and Cloudflare. I’ll need to deal with those separately, but I wanted urge caution here. As the console output doesn’t tell you about those. They’re just quietly omitted.

I took this solution and tweaked the output slightly to massage it into a valid CSV that KeePass with import without issue.

Here are the full steps to get from Authy to KeePass.

In Chrome (67.0.3396.99 as of this post, but later versions will probably work):

appManager.getModel().forEach(function(i){
  if(i.markedForDeletion === false){
    console.log('Root,'+i.name+',,'+i.decryptedSeed+',');
  }
});

After you have the resulting CSV, you can import it into KeePass. As far as I can tell, you cannot import a CSV into an existing KDB, you must create a new one. However, you can merge this new KDB into an existing KDB after the fact.

You can optionally confirm that the shared secret string that you have exported is generating the expected 2FA code, using oathtool.

On Mac:

brew install oath-toolkit
oathtool --totp --base32 <SHARED_SECRET_FROM_KEEPASS>

Compare this value to the value that you get in your existing Authy app.

Note: this shared secret is considered sensitive info! It is highly encouraged to prepend the oathtool command with a space, if your shell supports that as a way to not add it to your history, or otherwise remove the entry from your $HISTFILE, as appropriate.

Once everything is nestled nice and safe into KeePass, be sure to shred and delete the CSV and any temp files that may have been created.

gshred -u shared-secrets.csv tmpfile
tags: mfa - passwords