Closed jacobian closed 10 years ago
BTW, we shouldn't feel too bad about this, most other TOTP implementations are getting this wrong, too. Only Google and GitHub seem to have gotten it right :tongue:.
Great note! I was slightly surprised to find this is the case, that's why I was able to ask people to just run twofactor:enable
again today.
Lets definitely make that blow up the previous secret. Maybe the CLI can check if it's already enabled and show a warning?
Well, we don't want it to just blow up the existing code and replace it with a new one -- if we did that, I could disable 2fa by stealing someone's password and running twofactor:enable
. I think we probably need to force people to the website (where they'll have to log in using the old 2FA token or a backup code) and then have a way to generate a new token there.
Right! Or basically you need to pass a 2fa check before you can replace the old secret, right?
So actually, this is a matter of some urgency: turns out this makes 2FA pretty trivial to disable if you have someone's API token. Assume I own your laptop, now check this out:
$ HKHEADER='Accept: application/json' hk api GET /account/two-factor
{"enabled":true,"url":"otpauth://totp/Heroku:jacob@heroku.com?secret=XXXXXXXXXXXXX"}
Yup, there's the 2FA secret there, each time. So I think we need to do one or more of the following:
GET /account/two-factor
(and thus invalidate the previous secrets)GET /account/two-factor
.GET /account/two-factor
if 2FA is already enabled (thus making it just return the flag), and require a different endpoint to explicitly create a new token.Again this is pretty urgent, needs to fixed fairly soon; without this fix, 2FA is relatively easy to override if you steal someone's API token.
Started working on this here: https://github.com/heroku/api/pull/1969
Good job bringing up the options – ended up going for the second (more consistent, leave GET without side effects, etc).
Also noticed we were not validating for the token to allow users to disable 2fa, so that will change too. Will make sure this plugin still works fine with this.
Talked to JKM – we can't render the totp secret again or it will open the way to replay attacks.
Proposing some api tweaks and a new endpoint to address this:
POST /account/two-factor/url
(requires 2fa if enabled): resets your totp secret, renders a new urlPUT /account/two-factor
: (requires 2fa): enables 2fa on the accountDELETE /account/two-factor
: (requires 2fa): disables 2fa on the accountGET /account-two-factor
: renders 2fa status onlyWill do another review/deploy monday.
:+1: looks great to me.
@jacobian all deployed.
People will need to update the plugin in order to run 2fa:enable
again. Do you want me to email people or are you on it?
When you set up 2FA on a new device, it shouldn't generate the same tokens as the old device. heroku-two-factor gets this wrong, which means that if you set up a new device and forget to wipe the old one, you've got an old authenticator app floating around generating valid codes.
Steps to reproduce:
heroku twofactor:enable
, generating a barcode, and scan this barcode on Device B.There's an argument to be made that this is by design -- there's nothing particularly wrong with having multiple devices capable of generating valid TOTP codes -- but there does need to be a way to "switch" devices, getting new codes on Device B, and rendering Device A's codes invalid.
[Scumbag security developer, brags about getting a new phone by opening an issue against our 2FA implementation...]