I store my multifactor authentication recovery codes in plaintext on a flash drive. The flash drive is stored in a safe place that can only realistically be accessed by me. I periodically generate a PDF from these codes and print it.
There's a good amount of reading on ways that other people manage this:
- Best practices for storing 2FA recovery codes
- How to securely store and manage one-time backup codes for 2FA?
In choosing this approach, I considered not only the possibility that I would need to recover access to an MFA-protected account, but also the possibility that someone else might need to do so on my behalf (as part of my end-of-life planning). This made my approach the most suitable for several reasons:
- Codes are accessible without one of my configured MFA devices. (It would suck to need your second factor to disable your second factor.)
- Codes are stored separately from my credentials, so they are a true second factor. (There are use cases for configuring your password manager with 2FA tokens but they are not my use cases.)
- Because the codes are stored offline, physical presence would be required to retrieve them. (I'm not operating with Barton Gellman's threat model, so thankfully I can cut corners that others can't.)
This clearly isn't the most secure system, but I think it does a good job of balancing risk and accessibility. Maybe I'll look back in a couple months and realize how big of a mistake this is.
Each service gets its own file:
$ ls -l
total 88K
-rw-rw-r-- 1 natan natan 99 Jul 19 01:36 twitter
-rw-rw-r-- 1 natan natan 87 Jul 19 01:37 linkedin
-rw-rw-r-- 1 natan natan 191 Jul 19 01:39 github
...
$ cat github
0ef79f10-ca9f-491b-af08-6ff2b3b1846d
3600f8eb-671d-4f30-815d-7294e6a46552
89a6b8ec-e244-4708-aaac-ba7bc490a9c0
50cfbe27-c8b0-421b-a02f-1948e9decdebI
It's trivial to generate a PDF of these codes:
$ echo Multifactor authentication recovery codes\nGenerated $(date)\n > ../mfa.txt
$ awk '{print FILENAME, $0}' * | column -e >> ../mfa.txt
$ cd ..
$ cat mfa.txt
Multifactor authentication recovery codes
Generated Sun 19 Jul 2020 05:12:24 AM UTC
github 0ef79f10-ca9f-491b-af08-6ff2b3b1846d
github 3600f8eb-671d-4f30-815d-7294e6a46552
github 89a6b8ec-e244-4708-aaac-ba7bc490a9c0
github 50cfbe27-c8b0-421b-a02f-1948e9decdebI
linkedin a271d8f4-36a7-4030-a59f-acd61f57c232
linkedin 7d5ec7b8-0033-4249-a8de-472d684d7b51
linkedin f71c7c17-632c-46a4-9f37-60c2b734aecc
linkedin 98b36624-c170-44fd-8c5b-a85579623d37
twitter 0d6562cc-f9bb-4a85-8485-e5cd9fea31ea
twitter 57837976-2512-4027-b94a-1e72897f625e
twitter 54f4489d-a0f8-4087-87fb-ee1978d6dbd0
twitter 9fb83345-7bb9-4d07-b802-0b7ccda88394
$ cupsfilter mfa.txt > mfa.pdf
In the future, I might show the mtime of each file. But it seems redundant.
For reference, with Nix: